The minimum size of state data files. All state chunks smaller than that are stored inline in the root checkpoint metadata file.
+
20 kb
+
MemorySize
+
The minimum size of state data files. All state chunks smaller than that are stored inline in the root checkpoint metadata file. The max memory threshold for this configuration is 1MB.
state.backend.fs.write-buffer-size
diff --git a/docs/_includes/generated/client_configuration.html b/docs/_includes/generated/client_configuration.html
new file mode 100644
index 00000000000000..c623ddeb3bbad3
--- /dev/null
+++ b/docs/_includes/generated/client_configuration.html
@@ -0,0 +1,24 @@
+
+
+
+
Key
+
Default
+
Type
+
Description
+
+
+
+
+
client.retry-period
+
2 s
+
Duration
+
The interval (in ms) between consecutive retries of failed attempts to execute commands through the CLI or Flink's clients, wherever retry is supported (default 2sec).
Enable the slot spread out allocation strategy. This strategy tries to spread out the slots evenly across all available `TaskExecutors`.
-
cluster.io-executor.pool-size
+
cluster.io-pool.size
(none)
Integer
-
The pool size of io executor for cluster entry-point and mini cluster. It's undefined by default and will use the number of CPU cores (hardware contexts) that the cluster entry-point JVM has access to.
+
The size of the IO executor pool used by the cluster to execute blocking IO operations (Master as well as TaskManager processes). By default it will use 4 * the number of CPU cores (hardware contexts) that the cluster process has access to. Increasing the pool size allows to run more IO operations concurrently.
Whether to enable the JVM direct memory limit of the JobManager process (-XX:MaxDirectMemorySize). The limit will be set to the value of 'jobmanager.memory.off-heap.size' option.
+
jobmanager.memory.flink.size
(none)
@@ -48,7 +54,7 @@
jobmanager.memory.off-heap.size
128 mb
MemorySize
-
Off-heap Memory size for JobManager. The JVM direct memory limit of the Job Manager process (-XX:MaxDirectMemorySize) will be set to this value. This option covers all off-heap memory usage including direct and native memory allocation.
+
Off-heap Memory size for JobManager. This option covers all off-heap memory usage including direct and native memory allocation. The JVM direct memory limit of the JobManager process (-XX:MaxDirectMemorySize) will be set to this value if the limit is enabled by 'jobmanager.memory.enable-jvm-direct-memory-limit'.
Enables unaligned checkpoints, which greatly reduce checkpointing times under backpressure.
Unaligned checkpoints contain data stored in buffers as part of the checkpoint state, which allows checkpoint barriers to overtake these buffers. Thus, the checkpoint duration becomes independent of the current throughput as checkpoint barriers are effectively not embedded into the stream of data anymore.
Unaligned checkpoints can only be enabled if `execution.checkpointing.mode` is `EXACTLY_ONCE`
+
Enables unaligned checkpoints, which greatly reduce checkpointing times under backpressure.
Unaligned checkpoints contain data stored in buffers as part of the checkpoint state, which allows checkpoint barriers to overtake these buffers. Thus, the checkpoint duration becomes independent of the current throughput as checkpoint barriers are effectively not embedded into the stream of data anymore.
Unaligned checkpoints can only be enabled if `execution.checkpointing.mode` is `EXACTLY_ONCE` and if `execution.checkpointing.max-concurrent-checkpoints` is 1
The pool size of io executor for cluster entry-point and mini cluster. It's undefined by default and will use the number of CPU cores (hardware contexts) that the cluster entry-point JVM has access to.
+
The size of the IO executor pool used by the cluster to execute blocking IO operations (Master as well as TaskManager processes). By default it will use 4 * the number of CPU cores (hardware contexts) that the cluster process has access to. Increasing the pool size allows to run more IO operations concurrently.
cluster.registration.error-delay
@@ -58,7 +58,7 @@
jobmanager.execution.failover-strategy
-
region
+
"region"
String
This option specifies how the job computation recovers from task failures. Accepted values are:
'full': Restarts all tasks to recover the job.
'region': Restarts all tasks that could be affected by the task failure. More details can be found here.
The predefined settings for RocksDB DBOptions and ColumnFamilyOptions by Flink community. Current supported candidate predefined-options are DEFAULT, SPINNING_DISK_OPTIMIZED, SPINNING_DISK_OPTIMIZED_HIGH_MEM or FLASH_SSD_OPTIMIZED. Note that user customized options and options from the OptionsFactory are applied on top of these predefined ones.
+
The predefined settings for RocksDB DBOptions and ColumnFamilyOptions by Flink community. Current supported candidate predefined-options are DEFAULT, SPINNING_DISK_OPTIMIZED, SPINNING_DISK_OPTIMIZED_HIGH_MEM or FLASH_SSD_OPTIMIZED. Note that user customized options and options from the RocksDBOptionsFactory are applied on top of these predefined ones.
The minimum size of state data files. All state chunks smaller than that are stored inline in the root checkpoint metadata file.
+
20 kb
+
MemorySize
+
The minimum size of state data files. All state chunks smaller than that are stored inline in the root checkpoint metadata file. The max memory threshold for this configuration is 1MB.
This option specifies how the job computation recovers from task failures. Accepted values are:
'full': Restarts all tasks to recover the job.
'region': Restarts all tasks that could be affected by the task failure. More details can be found here.
+
+
jobmanager.memory.enable-jvm-direct-memory-limit
+
false
+
Boolean
+
Whether to enable the JVM direct memory limit of the JobManager process (-XX:MaxDirectMemorySize). The limit will be set to the value of 'jobmanager.memory.off-heap.size' option.
+
jobmanager.memory.flink.size
(none)
@@ -72,7 +78,7 @@
jobmanager.memory.off-heap.size
128 mb
MemorySize
-
Off-heap Memory size for JobManager. The JVM direct memory limit of the Job Manager process (-XX:MaxDirectMemorySize) will be set to this value. This option covers all off-heap memory usage including direct and native memory allocation.
+
Off-heap Memory size for JobManager. This option covers all off-heap memory usage including direct and native memory allocation. The JVM direct memory limit of the JobManager process (-XX:MaxDirectMemorySize) will be set to this value if the limit is enabled by 'jobmanager.memory.enable-jvm-direct-memory-limit'.
The default value depends on the actually running version. In general it looks like "flink:<FLINK_VERSION>-scala_<SCALA_VERSION>"
String
-
Image to use for Flink containers.
+
Image to use for Flink containers. The specified image must be based upon the same Apache Flink and Scala versions as used by the application. Visit https://hub.docker.com/_/flink?tab=tags for the images provided by the Flink project.
The amount of memory to be allocated by the Python framework. The sum of the value of this configuration and "python.fn-execution.buffer.memory.size" represents the total memory of a Python worker. The memory will be accounted as managed memory if the actual memory allocated to an operator is no less than the total memory of a Python worker. Otherwise, this configuration takes no effect.
+
+
python.fn-execution.memory.managed
+
false
+
Boolean
+
If set, the Python worker will configure itself to use the managed memory budget of the task slot. Otherwise, it will use the Off-Heap Memory of the task slot. In this case, users should set the Task Off-Heap Memory using the configuration key taskmanager.memory.task.off-heap.size. For each Python worker, the required Task Off-Heap Memory is the sum of the value of python.fn-execution.framework.memory.size and python.fn-execution.buffer.memory.size.
The predefined settings for RocksDB DBOptions and ColumnFamilyOptions by Flink community. Current supported candidate predefined-options are DEFAULT, SPINNING_DISK_OPTIMIZED, SPINNING_DISK_OPTIMIZED_HIGH_MEM or FLASH_SSD_OPTIMIZED. Note that user customized options and options from the OptionsFactory are applied on top of these predefined ones.
+
The predefined settings for RocksDB DBOptions and ColumnFamilyOptions by Flink community. Current supported candidate predefined-options are DEFAULT, SPINNING_DISK_OPTIMIZED, SPINNING_DISK_OPTIMIZED_HIGH_MEM or FLASH_SSD_OPTIMIZED. Note that user customized options and options from the RocksDBOptionsFactory are applied on top of these predefined ones.
state.backend.rocksdb.timer-service.factory
ROCKSDB
Enum
Possible values: [HEAP, ROCKSDB]
-
This determines the factory for timer service state implementation. Options are either HEAP (heap-based, default) or ROCKSDB for an implementation based on RocksDB .
+
This determines the factory for timer service state implementation. Options are either HEAP (heap-based) or ROCKSDB for an implementation based on RocksDB.
This determines the factory for timer service state implementation. Options are either HEAP (heap-based, default) or ROCKSDB for an implementation based on RocksDB .
+
This determines the factory for timer service state implementation. Options are either HEAP (heap-based) or ROCKSDB for an implementation based on RocksDB.
diff --git a/docs/_includes/sidenav.html b/docs/_includes/sidenav.html
index 65dcc92ce039ce..ed767f23a0f8cb 100644
--- a/docs/_includes/sidenav.html
+++ b/docs/_includes/sidenav.html
@@ -101,8 +101,6 @@
{%- assign active = true -%}
{%- elsif this.nav-id and active_nav_ids contains this.nav-id -%}
{%- assign active = true -%}
- {%- elsif this.always-expand -%}
- {%- assign active = true -%}
{%- else -%}
{%- assign active = false -%}
{%- endif -%}
@@ -119,7 +117,7 @@
{%- if children.size > 0 -%}
{%- assign children = (children[0].items | sort: "nav-pos") -%}
{%- capture collapse_target -%}"#collapse-{{ i }}" data-toggle="collapse"{%- if active -%} class="active"{%- endif -%}{%- endcapture -%}
- {%- capture expand -%}{%- endcapture %}
+ {%- capture expand -%}{%- unless active -%} {%- endunless -%}{%- endcapture %}
- Performs a left, right, or full outer join on two data sets. Outer joins are similar to regular (inner) joins and create all pairs of elements that are equal on their keys. In addition, records of the "outer" side (left, right, or both in case of full) are preserved if no matching key is found in the other side. Matching pairs of elements (or one element and a null value for the other input) are given to a JoinFunction to turn the pair of elements into a single element, or to a FlatJoinFunction to turn the pair of elements into arbitrarily many (including none) elements. See the keys section to learn how to define join keys.
+ Performs a left, right, or full outer join on two data sets. Outer joins are similar to regular (inner) joins and create all pairs of elements that are equal on their keys. In addition, records of the "outer" side (left, right, or both in case of full) are preserved if no matching key is found in the other side. Matching pairs of elements (or one element and a null value for the other input) are given to a JoinFunction to turn the pair of elements into a single element, or to a FlatJoinFunction to turn the pair of elements into arbitrarily many (including none) elements. See the keys section to learn how to define join keys.
{% highlight java %}
input1.leftOuterJoin(input2) // rightOuterJoin or fullOuterJoin for right or full outer joins
.where(0) // key of the first input (tuple field 0)
@@ -326,7 +327,7 @@ input1.leftOuterJoin(input2) // rightOuterJoin or fullOuterJoin for right or ful
The two-dimensional variant of the reduce operation. Groups each input on one or more
fields and then joins the groups. The transformation function is called per pair of groups.
- See the keys section to learn how to define coGroup keys.
+ See the keys section to learn how to define coGroup keys.
{% highlight java %}
data1.coGroup(data2)
.where(0)
@@ -600,7 +601,7 @@ data.distinct()
Joins two data sets by creating all pairs of elements that are equal on their keys.
Optionally uses a JoinFunction to turn the pair of elements into a single element, or a
FlatJoinFunction to turn the pair of elements into arbitrarily many (including none)
- elements. See the keys section to learn how to define join keys.
+ elements. See the keys section to learn how to define join keys.
{% highlight scala %}
// In this case tuple fields are used as keys. "0" is the join field on the first tuple
// "1" is the join field on the second tuple.
@@ -626,7 +627,7 @@ val result = input1.join(input2, JoinHint.BROADCAST_HASH_FIRST)
OuterJoin
- Performs a left, right, or full outer join on two data sets. Outer joins are similar to regular (inner) joins and create all pairs of elements that are equal on their keys. In addition, records of the "outer" side (left, right, or both in case of full) are preserved if no matching key is found in the other side. Matching pairs of elements (or one element and a `null` value for the other input) are given to a JoinFunction to turn the pair of elements into a single element, or to a FlatJoinFunction to turn the pair of elements into arbitrarily many (including none) elements. See the keys section to learn how to define join keys.
+ Performs a left, right, or full outer join on two data sets. Outer joins are similar to regular (inner) joins and create all pairs of elements that are equal on their keys. In addition, records of the "outer" side (left, right, or both in case of full) are preserved if no matching key is found in the other side. Matching pairs of elements (or one element and a `null` value for the other input) are given to a JoinFunction to turn the pair of elements into a single element, or to a FlatJoinFunction to turn the pair of elements into arbitrarily many (including none) elements. See the keys section to learn how to define join keys.
{% highlight scala %}
val joined = left.leftOuterJoin(right).where(0).equalTo(1) {
(left, right) =>
@@ -642,7 +643,7 @@ val joined = left.leftOuterJoin(right).where(0).equalTo(1) {
The two-dimensional variant of the reduce operation. Groups each input on one or more
fields and then joins the groups. The transformation function is called per pair of groups.
- See the keys section to learn how to define coGroup keys.
+ See the keys section to learn how to define coGroup keys.
{% highlight scala %}
data1.coGroup(data2).where(0).equalTo(1)
{% endhighlight %}
@@ -796,6 +797,230 @@ possible for [Data Sources](#data-sources) and [Data Sinks](#data-sinks).
{% top %}
+Specifying Keys
+---------------
+
+Some transformations (join, coGroup, groupBy) require that a key be defined on
+a collection of elements. Other transformations (Reduce, GroupReduce,
+Aggregate) allow data being grouped on a key before they are
+applied.
+
+A DataSet is grouped as
+{% highlight java %}
+DataSet<...> input = // [...]
+DataSet<...> reduced = input
+ .groupBy(/*define key here*/)
+ .reduceGroup(/*do something*/);
+{% endhighlight %}
+
+The data model of Flink is not based on key-value pairs. Therefore,
+you do not need to physically pack the data set types into keys and
+values. Keys are "virtual": they are defined as functions over the
+actual data to guide the grouping operator.
+
+### Define keys for Tuples
+{:.no_toc}
+
+The simplest case is grouping Tuples on one or more
+fields of the Tuple:
+
+
+
+Here, we group the tuples on a composite key consisting of the first and the
+second field.
+
+A note on nested Tuples: If you have a DataSet with a nested tuple, such as:
+
+{% highlight java %}
+DataSet,String,Long>> ds;
+{% endhighlight %}
+
+Specifying `groupBy(0)` will cause the system to use the full `Tuple2` as a key (with the Integer and Float being the key). If you want to "navigate" into the nested `Tuple2`, you have to use field expression keys which are explained below.
+
+### Define keys using Field Expressions
+{:.no_toc}
+
+You can use String-based field expressions to reference nested fields and define keys for grouping, sorting, joining, or coGrouping.
+
+Field expressions make it very easy to select fields in (nested) composite types such as [Tuple](#tuples-and-case-classes) and [POJO](#pojos) types.
+
+
+
+
+In the example below, we have a `WC` POJO with two fields "word" and "count". To group by the field `word`, we just pass its name to the `groupBy()` function.
+{% highlight java %}
+// some ordinary POJO (Plain old Java Object)
+public class WC {
+ public String word;
+ public int count;
+}
+DataSet words = // [...]
+DataSet wordCounts = words.groupBy("word")
+{% endhighlight %}
+
+**Field Expression Syntax**:
+
+- Select POJO fields by their field name. For example `"user"` refers to the "user" field of a POJO type.
+
+- Select Tuple fields by their field name or 0-offset field index. For example `"f0"` and `"5"` refer to the first and sixth field of a Java Tuple type, respectively.
+
+- You can select nested fields in POJOs and Tuples. For example `"user.zip"` refers to the "zip" field of a POJO which is stored in the "user" field of a POJO type. Arbitrary nesting and mixing of POJOs and Tuples is supported such as `"f1.user.zip"` or `"user.f3.1.zip"`.
+
+- You can select the full type using the `"*"` wildcard expressions. This does also work for types which are not Tuple or POJO types.
+
+**Field Expression Example**:
+
+{% highlight java %}
+public static class WC {
+ public ComplexNestedClass complex; //nested POJO
+ private int count;
+ // getter / setter for private field (count)
+ public int getCount() {
+ return count;
+ }
+ public void setCount(int c) {
+ this.count = c;
+ }
+}
+public static class ComplexNestedClass {
+ public Integer someNumber;
+ public float someFloat;
+ public Tuple3 word;
+ public IntWritable hadoopCitizen;
+}
+{% endhighlight %}
+
+These are valid field expressions for the example code above:
+
+- `"count"`: The count field in the `WC` class.
+
+- `"complex"`: Recursively selects all fields of the field complex of POJO type `ComplexNestedClass`.
+
+- `"complex.word.f2"`: Selects the last field of the nested `Tuple3`.
+
+- `"complex.hadoopCitizen"`: Selects the Hadoop `IntWritable` type.
+
+
+
+
+In the example below, we have a `WC` POJO with two fields "word" and "count". To group by the field `word`, we just pass its name to the `groupBy()` function.
+{% highlight scala %}
+// some ordinary POJO (Plain old Java Object)
+class WC(var word: String, var count: Int) {
+ def this() { this("", 0L) }
+}
+val words: DataSet[WC] = // [...]
+val wordCounts = words.groupBy("word")
+
+// or, as a case class, which is less typing
+case class WC(word: String, count: Int)
+val words: DataSet[WC] = // [...]
+val wordCounts = words.groupBy("word")
+{% endhighlight %}
+
+**Field Expression Syntax**:
+
+- Select POJO fields by their field name. For example `"user"` refers to the "user" field of a POJO type.
+
+- Select Tuple fields by their 1-offset field name or 0-offset field index. For example `"_1"` and `"5"` refer to the first and sixth field of a Scala Tuple type, respectively.
+
+- You can select nested fields in POJOs and Tuples. For example `"user.zip"` refers to the "zip" field of a POJO which is stored in the "user" field of a POJO type. Arbitrary nesting and mixing of POJOs and Tuples is supported such as `"_2.user.zip"` or `"user._4.1.zip"`.
+
+- You can select the full type using the `"_"` wildcard expressions. This does also work for types which are not Tuple or POJO types.
+
+**Field Expression Example**:
+
+{% highlight scala %}
+class WC(var complex: ComplexNestedClass, var count: Int) {
+ def this() { this(null, 0) }
+}
+
+class ComplexNestedClass(
+ var someNumber: Int,
+ someFloat: Float,
+ word: (Long, Long, String),
+ hadoopCitizen: IntWritable) {
+ def this() { this(0, 0, (0, 0, ""), new IntWritable(0)) }
+}
+{% endhighlight %}
+
+These are valid field expressions for the example code above:
+
+- `"count"`: The count field in the `WC` class.
+
+- `"complex"`: Recursively selects all fields of the field complex of POJO type `ComplexNestedClass`.
+
+- `"complex.word._3"`: Selects the last field of the nested `Tuple3`.
+
+- `"complex.hadoopCitizen"`: Selects the Hadoop `IntWritable` type.
+
+
+
+
+### Define keys using Key Selector Functions
+{:.no_toc}
+
+An additional way to define keys are "key selector" functions. A key selector function
+takes a single element as input and returns the key for the element. The key can be of any type and be derived from deterministic computations.
+
+The following example shows a key selector function that simply returns the field of an object:
+
+
+
+{% highlight java %}
+// some ordinary POJO
+public class WC {public String word; public int count;}
+DataSet words = // [...]
+UnsortedGrouping keyed = words
+ .groupBy(new KeySelector() {
+ public String getKey(WC wc) { return wc.word; }
+ });
+{% endhighlight %}
+
+
+
+{% highlight scala %}
+// some ordinary case class
+case class WC(word: String, count: Int)
+val words: DataSet[WC] = // [...]
+val keyed = words.groupBy( _.word )
+{% endhighlight %}
+
+
+
+{% top %}
+
Data Sources
------------
@@ -1199,7 +1424,7 @@ myResult.output(
#### Locally Sorted Output
-The output of a data sink can be locally sorted on specified fields in specified orders using [tuple field positions]({{ site.baseurl }}/dev/api_concepts.html#define-keys-for-tuples) or [field expressions]({{ site.baseurl }}/dev/api_concepts.html#define-keys-using-field-expressions). This works for every output format.
+The output of a data sink can be locally sorted on specified fields in specified orders using [tuple field positions](#define-keys-for-tuples) or [field expressions](#define-keys-using-field-expressions). This works for every output format.
The following examples show how to use this feature:
@@ -1282,7 +1507,7 @@ values map { tuple => tuple._1 + " - " + tuple._2 }
#### Locally Sorted Output
-The output of a data sink can be locally sorted on specified fields in specified orders using [tuple field positions]({{ site.baseurl }}/dev/api_concepts.html#define-keys-for-tuples) or [field expressions]({{ site.baseurl }}/dev/api_concepts.html#define-keys-using-field-expressions). This works for every output format.
+The output of a data sink can be locally sorted on specified fields in specified orders using [tuple field positions](#define-keys-for-tuples) or [field expressions](#define-keys-using-field-expressions). This works for every output format.
The following examples show how to use this feature:
@@ -1771,7 +1996,7 @@ This information is used by the optimizer to infer whether a data property such
partitioning is preserved by a function.
For functions that operate on groups of input elements such as `GroupReduce`, `GroupCombine`, `CoGroup`, and `MapPartition`, all fields that are defined as forwarded fields must always be jointly forwarded from the same input element. The forwarded fields of each element that is emitted by a group-wise function may originate from a different element of the function's input group.
-Field forward information is specified using [field expressions]({{ site.baseurl }}/dev/api_concepts.html#define-keys-using-field-expressions).
+Field forward information is specified using [field expressions](#define-keys-using-field-expressions).
Fields that are forwarded to the same position in the output can be specified by their position.
The specified position must be valid for the input and output data type and have the same type.
For example the String `"f2"` declares that the third field of a Java input tuple is always equal to the third field in the output tuple.
@@ -1840,7 +2065,7 @@ Non-forwarded field information for group-wise operators such as `GroupReduce`,
**IMPORTANT**: The specification of non-forwarded fields information is optional. However if used,
**ALL!** non-forwarded fields must be specified, because all other fields are considered to be forwarded in place. It is safe to declare a forwarded field as non-forwarded.
-Non-forwarded fields are specified as a list of [field expressions]({{ site.baseurl }}/dev/api_concepts.html#define-keys-using-field-expressions). The list can be either given as a single String with field expressions separated by semicolons or as multiple Strings.
+Non-forwarded fields are specified as a list of [field expressions](#define-keys-using-field-expressions). The list can be either given as a single String with field expressions separated by semicolons or as multiple Strings.
For example both `"f1; f3"` and `"f1", "f3"` declare that the second and fourth field of a Java tuple
are not preserved in place and all other fields are preserved in place.
Non-forwarded field information can only be specified for functions which have identical input and output types.
@@ -1891,7 +2116,7 @@ Fields which are only unmodified forwarded to the output without evaluating thei
**IMPORTANT**: The specification of read fields information is optional. However if used,
**ALL!** read fields must be specified. It is safe to declare a non-read field as read.
-Read fields are specified as a list of [field expressions]({{ site.baseurl }}/dev/api_concepts.html#define-keys-using-field-expressions). The list can be either given as a single String with field expressions separated by semicolons or as multiple Strings.
+Read fields are specified as a list of [field expressions](#define-keys-using-field-expressions). The list can be either given as a single String with field expressions separated by semicolons or as multiple Strings.
For example both `"f1; f3"` and `"f1", "f3"` declare that the second and fourth field of a Java tuple are read and evaluated by the function.
Read field information is specified as function class annotations using the following annotations:
@@ -2045,7 +2270,7 @@ DataSet result = input.map(new MyMapper());
env.execute();
{% endhighlight %}
-Access the cached file or directory in a user function (here a `MapFunction`). The function must extend a [RichFunction]({{ site.baseurl }}/dev/api_concepts.html#rich-functions) class because it needs access to the `RuntimeContext`.
+Access the cached file or directory in a user function (here a `MapFunction`). The function must extend a [RichFunction]({% link dev/user_defined_functions.zh.md %}#rich-functions) class because it needs access to the `RuntimeContext`.
{% highlight java %}
@@ -2091,7 +2316,7 @@ val result: DataSet[Integer] = input.map(new MyMapper())
env.execute()
{% endhighlight %}
-Access the cached file in a user function (here a `MapFunction`). The function must extend a [RichFunction]({{ site.baseurl }}/dev/api_concepts.html#rich-functions) class because it needs access to the `RuntimeContext`.
+Access the cached file in a user function (here a `MapFunction`). The function must extend a [RichFunction]({% link dev/user_defined_functions.zh.md %}#rich-functions) class because it needs access to the `RuntimeContext`.
{% highlight scala %}
@@ -2164,7 +2389,7 @@ class MyFilter(limit: Int) extends FilterFunction[Int] {
#### Via `withParameters(Configuration)`
-This method takes a Configuration object as an argument, which will be passed to the [rich function]({{ site.baseurl }}/dev/api_concepts.html#rich-functions)'s `open()`
+This method takes a Configuration object as an argument, which will be passed to the [rich function]({% link dev/user_defined_functions.zh.md %}#rich-functions)'s `open()`
method. The Configuration object is a Map from String keys to different value types.
diff --git a/docs/dev/cluster_execution.md b/docs/dev/cluster_execution.md
index 2b290549ff76d9..8d5d43701e8023 100644
--- a/docs/dev/cluster_execution.md
+++ b/docs/dev/cluster_execution.md
@@ -62,7 +62,7 @@ The following illustrates the use of the `RemoteEnvironment`:
{% highlight java %}
public static void main(String[] args) throws Exception {
ExecutionEnvironment env = ExecutionEnvironment
- .createRemoteEnvironment("flink-master", 8081, "/home/user/udfs.jar");
+ .createRemoteEnvironment("flink-jobmanager", 8081, "/home/user/udfs.jar");
DataSet data = env.readTextFile("hdfs://path/to/file");
diff --git a/docs/dev/cluster_execution.zh.md b/docs/dev/cluster_execution.zh.md
index 60b1e26909d83f..4613a6a565d8e5 100644
--- a/docs/dev/cluster_execution.zh.md
+++ b/docs/dev/cluster_execution.zh.md
@@ -62,7 +62,7 @@ The following illustrates the use of the `RemoteEnvironment`:
{% highlight java %}
public static void main(String[] args) throws Exception {
ExecutionEnvironment env = ExecutionEnvironment
- .createRemoteEnvironment("flink-master", 8081, "/home/user/udfs.jar");
+ .createRemoteEnvironment("flink-jobmanager", 8081, "/home/user/udfs.jar");
DataSet data = env.readTextFile("hdfs://path/to/file");
diff --git a/docs/dev/connectors/cassandra.md b/docs/dev/connectors/cassandra.md
index 002d388bcef778..6420799510f9b1 100644
--- a/docs/dev/connectors/cassandra.md
+++ b/docs/dev/connectors/cassandra.md
@@ -43,7 +43,7 @@ To use this connector, add the following dependency to your project:
{% endhighlight %}
-Note that the streaming connectors are currently __NOT__ part of the binary distribution. See how to link with them for cluster execution [here]({{ site.baseurl}}/dev/projectsetup/dependencies.html).
+Note that the streaming connectors are currently __NOT__ part of the binary distribution. See how to link with them for cluster execution [here]({{ site.baseurl}}/dev/project-configuration.html).
## Installing Apache Cassandra
There are multiple ways to bring up a Cassandra instance on local machine:
diff --git a/docs/dev/connectors/cassandra.zh.md b/docs/dev/connectors/cassandra.zh.md
index 9a51387ea2b7aa..c10ea49820b1c0 100644
--- a/docs/dev/connectors/cassandra.zh.md
+++ b/docs/dev/connectors/cassandra.zh.md
@@ -43,7 +43,7 @@ To use this connector, add the following dependency to your project:
{% endhighlight %}
-Note that the streaming connectors are currently __NOT__ part of the binary distribution. See how to link with them for cluster execution [here]({{ site.baseurl}}/dev/projectsetup/dependencies.html).
+Note that the streaming connectors are currently __NOT__ part of the binary distribution. See how to link with them for cluster execution [here]({{ site.baseurl}}/dev/project-configuration.html).
## Installing Apache Cassandra
There are multiple ways to bring up a Cassandra instance on local machine:
@@ -111,7 +111,7 @@ More details on [checkpoints docs]({{ site.baseurl }}/dev/stream/state/checkpoin
## Examples
-The Cassandra sinks currently support both Tuple and POJO data types, and Flink automatically detects which type of input is used. For general use case of those streaming data type, please refer to [Supported Data Types]({{ site.baseurl }}/dev/api_concepts.html). We show two implementations based on [SocketWindowWordCount](https://github.com/apache/flink/blob/master/flink-examples/flink-examples-streaming/src/main/java/org/apache/flink/streaming/examples/socket/SocketWindowWordCount.java), for Pojo and Tuple data types respectively.
+The Cassandra sinks currently support both Tuple and POJO data types, and Flink automatically detects which type of input is used. For general use case of those streaming data type, please refer to [Supported Data Types]({% link dev/types_serialization.zh.md %}#supported-data-types). We show two implementations based on [SocketWindowWordCount](https://github.com/apache/flink/blob/master/flink-examples/flink-examples-streaming/src/main/java/org/apache/flink/streaming/examples/socket/SocketWindowWordCount.java), for Pojo and Tuple data types respectively.
In all these examples, we assumed the associated Keyspace `example` and Table `wordcount` have been created.
diff --git a/docs/dev/connectors/elasticsearch.md b/docs/dev/connectors/elasticsearch.md
index 5bc1404e3a4a6d..b613d0bf8998ea 100644
--- a/docs/dev/connectors/elasticsearch.md
+++ b/docs/dev/connectors/elasticsearch.md
@@ -59,7 +59,7 @@ of the Elasticsearch installation:
Note that the streaming connectors are currently not part of the binary
-distribution. See [here]({{site.baseurl}}/dev/projectsetup/dependencies.html) for information
+distribution. See [here]({{site.baseurl}}/dev/project-configuration.html) for information
about how to package the program with the libraries for cluster execution.
## Installing Elasticsearch
@@ -317,7 +317,7 @@ time of checkpoints. This effectively assures that all requests before the
checkpoint was triggered have been successfully acknowledged by Elasticsearch, before
proceeding to process more records sent to the sink.
-More details on checkpoints and fault tolerance are in the [fault tolerance docs]({{site.baseurl}}/internals/stream_checkpointing.html).
+More details on checkpoints and fault tolerance are in the [fault tolerance docs]({{site.baseurl}}/learn-flink/fault_tolerance.html).
To use fault tolerant Elasticsearch Sinks, checkpointing of the topology needs to be enabled at the execution environment:
@@ -464,7 +464,7 @@ More information about Elasticsearch can be found [here](https://elastic.co).
For the execution of your Flink program, it is recommended to build a
so-called uber-jar (executable jar) containing all your dependencies
-(see [here]({{site.baseurl}}/dev/projectsetup/dependencies.html) for further information).
+(see [here]({{site.baseurl}}/dev/project-configuration.html) for further information).
Alternatively, you can put the connector's jar file into Flink's `lib/` folder to make it available
system-wide, i.e. for all job being run.
diff --git a/docs/dev/connectors/elasticsearch.zh.md b/docs/dev/connectors/elasticsearch.zh.md
index 59219543d83f47..e29c9208900333 100644
--- a/docs/dev/connectors/elasticsearch.zh.md
+++ b/docs/dev/connectors/elasticsearch.zh.md
@@ -59,7 +59,7 @@ of the Elasticsearch installation:
Note that the streaming connectors are currently not part of the binary
-distribution. See [here]({{site.baseurl}}/dev/projectsetup/dependencies.html) for information
+distribution. See [here]({{site.baseurl}}/dev/project-configuration.html) for information
about how to package the program with the libraries for cluster execution.
## Installing Elasticsearch
@@ -317,7 +317,7 @@ time of checkpoints. This effectively assures that all requests before the
checkpoint was triggered have been successfully acknowledged by Elasticsearch, before
proceeding to process more records sent to the sink.
-More details on checkpoints and fault tolerance are in the [fault tolerance docs]({{site.baseurl}}/internals/stream_checkpointing.html).
+More details on checkpoints and fault tolerance are in the [fault tolerance docs]({{site.baseurl}}/zh/learn-flink/fault_tolerance.html).
To use fault tolerant Elasticsearch Sinks, checkpointing of the topology needs to be enabled at the execution environment:
@@ -464,7 +464,7 @@ More information about Elasticsearch can be found [here](https://elastic.co).
For the execution of your Flink program, it is recommended to build a
so-called uber-jar (executable jar) containing all your dependencies
-(see [here]({{site.baseurl}}/dev/projectsetup/dependencies.html) for further information).
+(see [here]({{site.baseurl}}/dev/project-configuration.html) for further information).
Alternatively, you can put the connector's jar file into Flink's `lib/` folder to make it available
system-wide, i.e. for all job being run.
diff --git a/docs/dev/connectors/filesystem_sink.md b/docs/dev/connectors/filesystem_sink.md
index f24478f3609562..f58ebb36c8f349 100644
--- a/docs/dev/connectors/filesystem_sink.md
+++ b/docs/dev/connectors/filesystem_sink.md
@@ -42,7 +42,7 @@ following dependency to your project:
Note that the streaming connectors are currently not part of the binary
distribution. See
-[here]({{site.baseurl}}/dev/projectsetup/dependencies.html)
+[here]({{site.baseurl}}/dev/project-configuration.html)
for information about how to package the program with the libraries for
cluster execution.
diff --git a/docs/dev/connectors/filesystem_sink.zh.md b/docs/dev/connectors/filesystem_sink.zh.md
index 1e570e684123ca..9ae22faa5027d9 100644
--- a/docs/dev/connectors/filesystem_sink.zh.md
+++ b/docs/dev/connectors/filesystem_sink.zh.md
@@ -39,7 +39,7 @@ under the License.
{% endhighlight %}
-注意连接器目前还不是二进制发行版的一部分,添加依赖、打包配置以及集群运行信息请参考 [这里]({{site.baseurl}}/zh/dev/projectsetup/dependencies.html)。
+注意连接器目前还不是二进制发行版的一部分,添加依赖、打包配置以及集群运行信息请参考 [这里]({{site.baseurl}}/zh/dev/project-configuration.html)。
#### 分桶文件 Sink
diff --git a/docs/dev/connectors/jdbc.md b/docs/dev/connectors/jdbc.md
index 927a28fbf9e35c..d3fc286beb84e0 100644
--- a/docs/dev/connectors/jdbc.md
+++ b/docs/dev/connectors/jdbc.md
@@ -26,36 +26,6 @@ under the License.
* This will be replaced by the TOC
{:toc}
-
----
-title: "JDBC Connector"
-nav-title: JDBC
-nav-parent_id: connectors
-nav-pos: 9
----
-
-
-* This will be replaced by the TOC
-{:toc}
-
-
This connector provides a sink that writes data to a JDBC database.
To use it, add the following dependency to your project (along with your JDBC-driver):
@@ -68,7 +38,7 @@ To use it, add the following dependency to your project (along with your JDBC-dr
{% endhighlight %}
-Note that the streaming connectors are currently __NOT__ part of the binary distribution. See how to link with them for cluster execution [here]({{ site.baseurl}}/dev/projectsetup/dependencies.html).
+Note that the streaming connectors are currently __NOT__ part of the binary distribution. See how to link with them for cluster execution [here]({{ site.baseurl}}/dev/project-configuration.html).
Created JDBC sink provides at-least-once guarantee.
Effectively exactly-once can be achived using upsert statements or idempotent updates.
diff --git a/docs/dev/connectors/jdbc.zh.md b/docs/dev/connectors/jdbc.zh.md
index 927a28fbf9e35c..d3fc286beb84e0 100644
--- a/docs/dev/connectors/jdbc.zh.md
+++ b/docs/dev/connectors/jdbc.zh.md
@@ -26,36 +26,6 @@ under the License.
* This will be replaced by the TOC
{:toc}
-
----
-title: "JDBC Connector"
-nav-title: JDBC
-nav-parent_id: connectors
-nav-pos: 9
----
-
-
-* This will be replaced by the TOC
-{:toc}
-
-
This connector provides a sink that writes data to a JDBC database.
To use it, add the following dependency to your project (along with your JDBC-driver):
@@ -68,7 +38,7 @@ To use it, add the following dependency to your project (along with your JDBC-dr
{% endhighlight %}
-Note that the streaming connectors are currently __NOT__ part of the binary distribution. See how to link with them for cluster execution [here]({{ site.baseurl}}/dev/projectsetup/dependencies.html).
+Note that the streaming connectors are currently __NOT__ part of the binary distribution. See how to link with them for cluster execution [here]({{ site.baseurl}}/dev/project-configuration.html).
Created JDBC sink provides at-least-once guarantee.
Effectively exactly-once can be achived using upsert statements or idempotent updates.
diff --git a/docs/dev/connectors/kafka.md b/docs/dev/connectors/kafka.md
index d084808c01f8da..f2e722f1417142 100644
--- a/docs/dev/connectors/kafka.md
+++ b/docs/dev/connectors/kafka.md
@@ -23,125 +23,59 @@ specific language governing permissions and limitations
under the License.
-->
+Flink provides an [Apache Kafka](https://kafka.apache.org) connector for reading data from and writing data to Kafka topics with exactly-once guaruntees.
+
* This will be replaced by the TOC
{:toc}
-This connector provides access to event streams served by [Apache Kafka](https://kafka.apache.org/).
-
-Flink provides special Kafka Connectors for reading and writing data from/to Kafka topics.
-The Flink Kafka Consumer integrates with Flink's checkpointing mechanism to provide
-exactly-once processing semantics. To achieve that, Flink does not purely rely on Kafka's consumer group
-offset tracking, but tracks and checkpoints these offsets internally as well.
-
-Please pick a package (maven artifact id) and class name for your use-case and environment.
-For most users, the `FlinkKafkaConsumer010` (part of `flink-connector-kafka`) is appropriate.
-
-
Since 0.11.x Kafka does not support scala 2.10. This connector supports Kafka transactional messaging to provide exactly once semantic for the producer.
- This universal Kafka connector attempts to track the latest version of the Kafka client.
- The version of the client it uses may change between Flink releases. Starting with Flink 1.9 release, it uses the Kafka 2.2.0 client.
- Modern Kafka clients are backwards compatible with broker versions 0.10.0 or later.
- However for Kafka 0.11.x and 0.10.x versions, we recommend using dedicated
- flink-connector-kafka-0.11{{ site.scala_version_suffix }} and flink-connector-kafka-0.10{{ site.scala_version_suffix }} respectively.
-
-
-
-
-
-Then, import the connector in your maven project:
+## Dependency
+
+Apache Flink ships with multiple Kafka connectors: universal, 0.10, and 0.11.
+This universal Kafka connector attempts to track the latest version of the Kafka client.
+The version of the client it uses may change between Flink releases.
+Modern Kafka clients are backwards compatible with broker versions 0.10.0 or later.
+For most users the universal Kafka connector is the most appropriate.
+However, for Kafka versions 0.11.x and 0.10.x, we recommend using the dedicated ``0.11`` and ``0.10`` connectors, respectively.
+For details on Kafka compatibility, please refer to the official [Kafka documentation](https://kafka.apache.org/protocol.html#protocol_compatibility).
+
+{% highlight xml %}
+
+ org.apache.flink
+ flink-connector-kafka-011{{ site.scala_version_suffix }}
+ {{ site.version }}
{% endhighlight %}
-
-Note that the streaming connectors are currently not part of the binary distribution.
-See how to link with them for cluster execution [here]({{ site.baseurl}}/dev/projectsetup/dependencies.html).
-
-## Installing Apache Kafka
-
-* Follow the instructions from [Kafka's quickstart](https://kafka.apache.org/documentation.html#quickstart) to download the code and launch a server (launching a Zookeeper and a Kafka server is required every time before starting the application).
-* If the Kafka and Zookeeper servers are running on a remote machine, then the `advertised.host.name` setting in the `config/server.properties` file must be set to the machine's IP address.
-
-## Kafka 1.0.0+ Connector
-
-Starting with Flink 1.7, there is a new universal Kafka connector that does not track a specific Kafka major version.
-Rather, it tracks the latest version of Kafka at the time of the Flink release.
-
-If your Kafka broker version is 1.0.0 or newer, you should use this Kafka connector.
-If you use an older version of Kafka (0.11 or 0.10), you should use the connector corresponding to the broker version.
-
-### Compatibility
-
-The universal Kafka connector is compatible with older and newer Kafka brokers through the compatibility guarantees of the Kafka client API and broker.
-It is compatible with broker versions 0.11.0 or newer, depending on the features used.
-For details on Kafka compatibility, please refer to the [Kafka documentation](https://kafka.apache.org/protocol.html#protocol_compatibility).
-
-### Migrating Kafka Connector from 0.11 to universal
-
-In order to perform the migration, see the [upgrading jobs and Flink versions guide]({{ site.baseurl }}/ops/upgrading.html)
-and:
-* Use Flink 1.9 or newer for the whole process.
-* Do not upgrade the Flink and operators at the same time.
-* Make sure that Kafka Consumer and/or Kafka Producer used in your job have assigned unique identifiers (`uid`):
-* Use stop with savepoint feature to take the savepoint (for example by using `stop --withSavepoint`)[CLI command]({{ site.baseurl }}/ops/cli.html).
-
-### Usage
-
-To use the universal Kafka connector add a dependency to it:
-
+
+
{% highlight xml %}
- org.apache.flink
- flink-connector-kafka{{ site.scala_version_suffix }}
- {{ site.version }}
+ org.apache.flink
+ flink-connector-kafka-010{{ site.scala_version_suffix }}
+ {{ site.version }}
{% endhighlight %}
+Attention The ``0.10`` sink does not support exactly-once writes to Kafka.
+
+
-Then instantiate the new source (`FlinkKafkaConsumer`) and sink (`FlinkKafkaProducer`).
-The API is backward compatible with the Kafka 0.11 connector,
-except of dropping specific Kafka version from the module and class names.
+Flink's streaming connectors are not currently part of the binary distribution.
+See how to link with them for cluster execution [here]({{ site.baseurl}}/dev/project-configuration.html).
## Kafka Consumer
-Flink's Kafka consumer is called `FlinkKafkaConsumer010` (or 011 for Kafka 0.11.0.x versions, etc.
-or just `FlinkKafkaConsumer` for Kafka >= 1.0.0 versions). It provides access to one or more Kafka topics.
+Flink's Kafka consumer - `FlinkKafkaConsumer` (or `FlinkKafkaConsumer011` for Kafka 0.11.x,
+or `FlinkKafkaConsumer010` for Kafka 0.10.x) - provides access to read from one or more Kafka topics.
The constructor accepts the following arguments:
@@ -152,8 +86,6 @@ The constructor accepts the following arguments:
- "bootstrap.servers" (comma separated list of Kafka brokers)
- "group.id" the id of the consumer group
-Example:
-
@@ -170,26 +102,17 @@ val properties = new Properties()
properties.setProperty("bootstrap.servers", "localhost:9092")
properties.setProperty("group.id", "test")
stream = env
- .addSource(new FlinkKafkaConsumer010[String]("topic", new SimpleStringSchema(), properties))
- .print()
+ .addSource(new FlinkKafkaConsumer[String]("topic", new SimpleStringSchema(), properties))
{% endhighlight %}
### The `DeserializationSchema`
-The Flink Kafka Consumer needs to know how to turn the binary data in Kafka into Java/Scala objects. The
-`DeserializationSchema` allows users to specify such a schema. The `T deserialize(byte[] message)`
-method gets called for each Kafka message, passing the value from Kafka.
-
-It is usually helpful to start from the `AbstractDeserializationSchema`, which takes care of describing the
-produced Java/Scala type to Flink's type system. Users that implement a vanilla `DeserializationSchema` need
-to implement the `getProducedType(...)` method themselves.
+The Flink Kafka Consumer needs to know how to turn the binary data in Kafka into Java/Scala objects.
+The `KafkaDeserializationSchema` allows users to specify such a schema. The `T deserialize(ConsumerRecord record)` method gets called for each Kafka message, passing the value from Kafka.
-For accessing the key, value and metadata of the Kafka message, the `KafkaDeserializationSchema` has
-the following deserialize method `T deserialize(ConsumerRecord record)`.
-
-For convenience, Flink provides the following schemas:
+For convenience, Flink provides the following schemas out of the box:
1. `TypeInformationSerializationSchema` (and `TypeInformationKeyValueSerializationSchema`) which creates
a schema based on a Flink's `TypeInformation`. This is useful if the data is both written and read by Flink.
@@ -216,46 +139,37 @@ For convenience, Flink provides the following schemas:
-When encountering a corrupted message that cannot be deserialized for any reason, there
-are two options - either throwing an exception from the `deserialize(...)` method
-which will cause the job to fail and be restarted, or returning `null` to allow
-the Flink Kafka consumer to silently skip the corrupted message. Note that
-due to the consumer's fault tolerance (see below sections for more details),
-failing the job on the corrupted message will let the consumer attempt
-to deserialize the message again. Therefore, if deserialization still fails, the
-consumer will fall into a non-stop restart and fail loop on that corrupted
-message.
+When encountering a corrupted message that cannot be deserialized for any reason the deserialization schema should return null which will result in the record being skipped.
+Due to the consumer's fault tolerance (see below sections for more details), failing the job on the corrupted message will let the consumer attempt to deserialize the message again.
+Therefore, if deserialization still fails, the consumer will fall into a non-stop restart and fail loop on that corrupted message.
### Kafka Consumers Start Position Configuration
-The Flink Kafka Consumer allows configuring how the start position for Kafka
-partitions are determined.
-
-Example:
+The Flink Kafka Consumer allows configuring how the start positions for Kafka partitions are determined.
{% highlight java %}
final StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
-FlinkKafkaConsumer010 myConsumer = new FlinkKafkaConsumer010<>(...);
+FlinkKafkaConsumer myConsumer = new FlinkKafkaConsumer<>(...);
myConsumer.setStartFromEarliest(); // start from the earliest record possible
myConsumer.setStartFromLatest(); // start from the latest record
myConsumer.setStartFromTimestamp(...); // start from specified epoch timestamp (milliseconds)
@@ -269,7 +183,7 @@ DataStream stream = env.addSource(myConsumer);
{% highlight scala %}
val env = StreamExecutionEnvironment.getExecutionEnvironment()
-val myConsumer = new FlinkKafkaConsumer010[String](...)
+val myConsumer = new FlinkKafkaConsumer[String](...)
myConsumer.setStartFromEarliest() // start from the earliest record possible
myConsumer.setStartFromLatest() // start from the latest record
myConsumer.setStartFromTimestamp(...) // start from specified epoch timestamp (milliseconds)
@@ -339,34 +253,14 @@ fault tolerance for the consumer).
### Kafka Consumers and Fault Tolerance
With Flink's checkpointing enabled, the Flink Kafka Consumer will consume records from a topic and periodically checkpoint all
-its Kafka offsets, together with the state of other operations, in a consistent manner. In case of a job failure, Flink will restore
+its Kafka offsets, together with the state of other operations. In case of a job failure, Flink will restore
the streaming program to the state of the latest checkpoint and re-consume the records from Kafka, starting from the offsets that were
stored in the checkpoint.
The interval of drawing checkpoints therefore defines how much the program may have to go back at most, in case of a failure.
+To use fault tolerant Kafka Consumers, checkpointing of the topology needs to be enabled in the [job]({{ site.baseurl }}/ops/config.html#execution-checkpointing-interval).
-To use fault tolerant Kafka Consumers, checkpointing of the topology needs to be enabled at the execution environment:
-
-
-
-Also note that Flink can only restart the topology if enough processing slots are available to restart the topology.
-So if the topology fails due to loss of a TaskManager, there must still be enough slots available afterwards.
-Flink on YARN supports automatic restart of lost YARN containers.
-
-If checkpointing is not enabled, the Kafka consumer will periodically commit the offsets to Zookeeper.
+If checkpointing is disabled, the Kafka consumer will periodically commit the offsets to Zookeeper.
### Kafka Consumers Topic and Partition Discovery
@@ -380,15 +274,9 @@ By default, partition discovery is disabled. To enable it, set a non-negative va
for `flink.partition-discovery.interval-millis` in the provided properties config,
representing the discovery interval in milliseconds.
-Limitation When the consumer is restored from a savepoint from Flink versions
-prior to Flink 1.3.x, partition discovery cannot be enabled on the restore run. If enabled, the restore would fail
-with an exception. In this case, in order to use partition discovery, please first take a savepoint in Flink 1.3.x and
-then restore again from that.
-
#### Topic discovery
-At a higher-level, the Flink Kafka Consumer is also capable of discovering topics, based on pattern matching on the
-topic names using regular expressions. See the below for an example:
+The Kafka Consumer is also capable of discovering topics by matching topic names using regular expressions.
@@ -399,7 +287,7 @@ Properties properties = new Properties();
properties.setProperty("bootstrap.servers", "localhost:9092");
properties.setProperty("group.id", "test");
-FlinkKafkaConsumer011 myConsumer = new FlinkKafkaConsumer011<>(
+FlinkKafkaConsumer myConsumer = new FlinkKafkaConsumer<>(
java.util.regex.Pattern.compile("test-topic-[0-9]"),
new SimpleStringSchema(),
properties);
@@ -416,7 +304,7 @@ val properties = new Properties()
properties.setProperty("bootstrap.servers", "localhost:9092")
properties.setProperty("group.id", "test")
-val myConsumer = new FlinkKafkaConsumer010[String](
+val myConsumer = new FlinkKafkaConsumer[String](
java.util.regex.Pattern.compile("test-topic-[0-9]"),
new SimpleStringSchema,
properties)
@@ -445,7 +333,7 @@ tolerance guarantees. The committed offsets are only a means to expose
the consumer's progress for monitoring purposes.
The way to configure offset commit behaviour is different, depending on
-whether or not checkpointing is enabled for the job.
+whether checkpointing is enabled for the job.
- *Checkpointing disabled:* if checkpointing is disabled, the Flink Kafka
Consumer relies on the automatic periodic offset committing capability
@@ -465,15 +353,14 @@ whether or not checkpointing is enabled for the job.
### Kafka Consumers and Timestamp Extraction/Watermark Emission
-In many scenarios, the timestamp of a record is embedded (explicitly or implicitly) in the record itself.
-In addition, the user may want to emit watermarks either periodically, or in an irregular fashion, e.g. based on
+In many scenarios, the timestamp of a record is embedded in the record itself, or the metadata of the `ConsumerRecord`.
+In addition, users may want to emit watermarks either periodically, or irregularly, e.g. based on
special records in the Kafka stream that contain the current event-time watermark. For these cases, the Flink Kafka
-Consumer allows the specification of an `AssignerWithPeriodicWatermarks` or an `AssignerWithPunctuatedWatermarks`.
+Consumer allows the specification of a [watermark strategy]({% link dev/event_time.md %}).
-You can specify your custom timestamp extractor/watermark emitter as described
-[here]({{ site.baseurl }}/dev/event_timestamps_watermarks.html), or use one from the
-[predefined ones]({{ site.baseurl }}/dev/event_timestamp_extractors.html). After doing so, you
-can pass it to your consumer in the following way:
+You can specify your custom strategy as described
+[here]({% link dev/event_timestamps_watermarks.md %}), or use one from the
+[predefined ones]({% link dev/event_timestamp_extractors.md %}).
@@ -497,51 +384,50 @@ val properties = new Properties()
properties.setProperty("bootstrap.servers", "localhost:9092")
properties.setProperty("group.id", "test")
-val myConsumer = new FlinkKafkaConsumer010[String]("topic", new SimpleStringSchema(), properties)
-myConsumer.assignTimestampsAndWatermarks(new CustomWatermarkEmitter())
-stream = env
- .addSource(myConsumer)
- .print()
+val myConsumer =
+ new FlinkKafkaConsumer("topic", new SimpleStringSchema(), properties);
+myConsumer.assignTimestampsAndWatermarks(
+ WatermarkStrategy.
+ .forBoundedOutOfOrderness(Duration.ofSeconds(20)))
+
+val stream = env.addSource(myConsumer)
{% endhighlight %}
-Internally, an instance of the assigner is executed per Kafka partition.
-When such an assigner is specified, for each record read from Kafka, the
-`extractTimestamp(T element, long previousElementTimestamp)` is called to assign a timestamp to the record and
-the `Watermark getCurrentWatermark()` (for periodic) or the
-`Watermark checkAndGetNextWatermark(T lastElement, long extractedTimestamp)` (for punctuated) is called to determine
-if a new watermark should be emitted and with which timestamp.
**Note**: If a watermark assigner depends on records read from Kafka to advance its watermarks
(which is commonly the case), all topics and partitions need to have a continuous stream of records.
Otherwise, the watermarks of the whole application cannot advance and all time-based operations,
such as time windows or functions with timers, cannot make progress. A single idle Kafka partition causes this behavior.
-A Flink improvement is planned to prevent this from happening
-(see [FLINK-5479: Per-partition watermarks in FlinkKafkaConsumer should consider idle partitions](
-https://issues.apache.org/jira/browse/FLINK-5479)).
-In the meanwhile, a possible workaround is to send *heartbeat messages* to all consumed partitions that advance the watermarks of idle partitions.
-
+Consider setting appropriate [idelness timeouts]({{ site.baseurl }}/dev/event_timestamps_watermarks.html#dealing-with-idle-sources) to mitigate this issue.
+
## Kafka Producer
-Flink’s Kafka Producer is called `FlinkKafkaProducer011` (or `010` for Kafka 0.10.0.x versions, etc. or just `FlinkKafkaProducer` for Kafka >= 1.0.0 versions).
-It allows writing a stream of records to one or more Kafka topics.
+Flink’s Kafka Producer - `FlinkKafkaProducer` (or `FlinkKafkaProducer010` for Kafka 0.10.x versions or `FlinkKafkaProducer011` for Kafka 0.11.x versions) -
+ allows writing a stream of records to one or more Kafka topics.
+
+The constructor accepts the following arguments:
-Example:
+1. A default output topic where events should be written
+2. A SerializationSchema / KafkaSerializationSchema for serializing data into Kafka
+3. Properties for the Kafka client. The following properties are required:
+ * "bootstrap.servers" (comma separated list of Kafka brokers)
+4. A fault-tolerance semantic
{% highlight java %}
-DataStream stream = ...;
+DataStream stream = ...
-FlinkKafkaProducer011 myProducer = new FlinkKafkaProducer011(
- "localhost:9092", // broker list
- "my-topic", // target topic
- new SimpleStringSchema()); // serialization schema
+Properties properties = new Properties();
+properties.setProperty("bootstrap.servers", "localhost:9092");
-// versions 0.10+ allow attaching the records' event timestamp when writing them to Kafka;
-// this method is not available for earlier Kafka versions
-myProducer.setWriteTimestampToKafka(true);
+FlinkKafkaProducer myProducer = new FlinkKafkaProducer<>(
+ "my-topic", // target topic
+ new SimpleStringSchema(), // serialization schema
+ properties, // producer config
+ FlinkKafkaProducer.Semantic.EXACTLY_ONCE); // fault-tolerance
stream.addSink(myProducer);
{% endhighlight %}
@@ -550,91 +436,36 @@ stream.addSink(myProducer);
{% highlight scala %}
val stream: DataStream[String] = ...
-val myProducer = new FlinkKafkaProducer011[String](
- "localhost:9092", // broker list
- "my-topic", // target topic
- new SimpleStringSchema) // serialization schema
+Properties properties = new Properties
+properties.setProperty("bootstrap.servers", "localhost:9092")
-// versions 0.10+ allow attaching the records' event timestamp when writing them to Kafka;
-// this method is not available for earlier Kafka versions
-myProducer.setWriteTimestampToKafka(true)
+val myProducer = new FlinkKafkaProducer[String](
+ "my-topic", // target topic
+ new SimpleStringSchema(), // serialization schema
+ properties, // producer config
+ FlinkKafkaProducer.Semantic.EXACTLY_ONCE) // fault-tolerance
stream.addSink(myProducer)
{% endhighlight %}
-The above examples demonstrate the basic usage of creating a Flink Kafka Producer
-to write streams to a single Kafka target topic. For more advanced usages, there
-are other constructor variants that allow providing the following:
-
- * *Providing custom properties*:
- The producer allows providing a custom properties configuration for the internal `KafkaProducer`.
- Please refer to the [Apache Kafka documentation](https://kafka.apache.org/documentation.html) for
- details on how to configure Kafka Producers.
- * *Custom partitioner*: To assign records to specific
- partitions, you can provide an implementation of a `FlinkKafkaPartitioner` to the
- constructor. This partitioner will be called for each record in the stream
- to determine which exact partition of the target topic the record should be sent to.
- Please see [Kafka Producer Partitioning Scheme](#kafka-producer-partitioning-scheme) for more details.
- * *Advanced serialization schema*: Similar to the consumer,
- the producer also allows using an advanced serialization schema called `KeyedSerializationSchema`,
- which allows serializing the key and value separately. It also allows to override the target topic,
- so that one producer instance can send data to multiple topics.
-
-### Kafka Producer Partitioning Scheme
-
-By default, if a custom partitioner is not specified for the Flink Kafka Producer, the producer will use
-a `FlinkFixedPartitioner` that maps each Flink Kafka Producer parallel subtask to a single Kafka partition
-(i.e., all records received by a sink subtask will end up in the same Kafka partition).
-
-A custom partitioner can be implemented by extending the `FlinkKafkaPartitioner` class. All
-Kafka versions' constructors allow providing a custom partitioner when instantiating the producer.
-Note that the partitioner implementation must be serializable, as they will be transferred across Flink nodes.
-Also, keep in mind that any state in the partitioner will be lost on job failures since the partitioner
-is not part of the producer's checkpointed state.
-
-It is also possible to completely avoid using and kind of partitioner, and simply let Kafka partition
-the written records by their attached key (as determined for each record using the provided serialization schema).
-To do this, provide a `null` custom partitioner when instantiating the producer. It is important
-to provide `null` as the custom partitioner; as explained above, if a custom partitioner is not specified
-the `FlinkFixedPartitioner` is used instead.
-
-### Kafka Producers and Fault Tolerance
+## The `SerializationSchema`
-#### Kafka 0.10
+The Flink Kafka Producer needs to know how to turn Java/Scala objects into binary data.
+The `KafkaSerializationSchema` allows users to specify such a schema.
+The `ProducerRecord serialize(T element, @Nullable Long timestamp)` method gets called for each record, generating a `ProducerRecord` that is written to Kafka.
-With Flink's checkpointing enabled, the `FlinkKafkaProducer010`
-can provide at-least-once delivery guarantees.
+The gives users fine-grained control over how data is written out to Kafka.
+Through the producer record you can:
+* Set header values
+* Define keys for each record
+* Specify custom partitioning of data
-Besides enabling Flink's checkpointing, you should also configure the setter
-methods `setLogFailuresOnly(boolean)` and `setFlushOnCheckpoint(boolean)` appropriately.
-
- * `setLogFailuresOnly(boolean)`: by default, this is set to `false`.
- Enabling this will let the producer only log failures
- instead of catching and rethrowing them. This essentially accounts the record
- to have succeeded, even if it was never written to the target Kafka topic. This
- must be disabled for at-least-once.
- * `setFlushOnCheckpoint(boolean)`: by default, this is set to `true`.
- With this enabled, Flink's checkpoints will wait for any
- on-the-fly records at the time of the checkpoint to be acknowledged by Kafka before
- succeeding the checkpoint. This ensures that all records before the checkpoint have
- been written to Kafka. This must be enabled for at-least-once.
-
-In conclusion, the Kafka producer by default has at-least-once guarantees for versions
-0.10, with `setLogFailureOnly` set to `false` and `setFlushOnCheckpoint` set
-to `true`.
-
-**Note**: By default, the number of retries is set to "0". This means that when `setLogFailuresOnly` is set to `false`,
-the producer fails immediately on errors, including leader changes. The value is set to "0" by default to avoid
-duplicate messages in the target topic that are caused by retries. For most production environments with frequent broker changes,
-we recommend setting the number of retries to a higher value.
-
-**Note**: There is currently no transactional producer for Kafka, so Flink can not guarantee exactly-once delivery
-into a Kafka topic.
-
-#### Kafka 0.11 and newer
+### Kafka Producers and Fault Tolerance
+
+
With Flink's checkpointing enabled, the `FlinkKafkaProducer011` (`FlinkKafkaProducer` for Kafka >= 1.0.0 versions) can provide
exactly-once delivery guarantees.
@@ -643,9 +474,8 @@ chosen by passing appropriate `semantic` parameter to the `FlinkKafkaProducer011
* `Semantic.NONE`: Flink will not guarantee anything. Produced records can be lost or they can
be duplicated.
- * `Semantic.AT_LEAST_ONCE` (default setting): similar to `setFlushOnCheckpoint(true)` in
- `FlinkKafkaProducer010`. This guarantees that no records will be lost (although they can be duplicated).
- * `Semantic.EXACTLY_ONCE`: uses Kafka transactions to provide exactly-once semantic. Whenever you write
+ * `Semantic.AT_LEAST_ONCE` (default setting): This guarantees that no records will be lost (although they can be duplicated).
+ * `Semantic.EXACTLY_ONCE`: Kafka transactions will be used to provide exactly-once semantic. Whenever you write
to Kafka using transactions, do not forget about setting desired `isolation.level` (`read_committed`
or `read_uncommitted` - the latter one is the default value) for any application consuming records
from Kafka.
@@ -695,54 +525,53 @@ event of failure of Flink application before first checkpoint, after restarting
is no information in the system about previous pool sizes. Thus it is unsafe to scale down Flink
application before first checkpoint completes, by factor larger than `FlinkKafkaProducer011.SAFE_SCALE_DOWN_FACTOR`.
-## Using Kafka timestamps and Flink event time in Kafka 0.10
-
-Since Apache Kafka 0.10+, Kafka's messages can carry
-[timestamps](https://cwiki.apache.org/confluence/display/KAFKA/KIP-32+-+Add+timestamps+to+Kafka+message), indicating
-the time the event has occurred (see ["event time" in Apache Flink](../event_time.html)) or the time when the message
-has been written to the Kafka broker.
-
-The `FlinkKafkaConsumer010` will emit records with the timestamp attached, if the time characteristic in Flink is
-set to `TimeCharacteristic.EventTime` (`StreamExecutionEnvironment.setStreamTimeCharacteristic(TimeCharacteristic.EventTime)`).
-
-The Kafka consumer does not emit watermarks. To emit watermarks, the same mechanisms as described above in
-"Kafka Consumers and Timestamp Extraction/Watermark Emission" using the `assignTimestampsAndWatermarks` method are applicable.
-
-There is no need to define a timestamp extractor when using the timestamps from Kafka. The `previousElementTimestamp` argument of
-the `extractTimestamp()` method contains the timestamp carried by the Kafka message.
-
-A timestamp extractor for a Kafka consumer would look like this:
-{% highlight java %}
-public long extractTimestamp(Long element, long previousElementTimestamp) {
- return previousElementTimestamp;
-}
-{% endhighlight %}
-
+
+
+With Flink's checkpointing enabled, the `FlinkKafkaProducer010`
+can provide at-least-once delivery guarantees.
+Besides enabling Flink's checkpointing, you should also configure the setter
+methods `setLogFailuresOnly(boolean)` and `setFlushOnCheckpoint(boolean)` appropriately.
-The `FlinkKafkaProducer010` only emits the record timestamp, if `setWriteTimestampToKafka(true)` is set.
+ * `setLogFailuresOnly(boolean)`: by default, this is set to `false`.
+ Enabling this will let the producer only log failures
+ instead of catching and rethrowing them. This essentially accounts the record
+ to have succeeded, even if it was never written to the target Kafka topic. This
+ must be disabled for at-least-once.
+ * `setFlushOnCheckpoint(boolean)`: by default, this is set to `true`.
+ With this enabled, Flink's checkpoints will wait for any
+ on-the-fly records at the time of the checkpoint to be acknowledged by Kafka before
+ succeeding the checkpoint. This ensures that all records before the checkpoint have
+ been written to Kafka. This must be enabled for at-least-once.
+
+In conclusion, the Kafka producer by default has at-least-once guarantees for versions
+0.10, with `setLogFailureOnly` set to `false` and `setFlushOnCheckpoint` set
+to `true`.
-{% highlight java %}
-FlinkKafkaProducer010.FlinkKafkaProducer010Configuration config = FlinkKafkaProducer010.writeToKafkaWithTimestamps(streamWithTimestamps, topic, new SimpleStringSchema(), standardProps);
-config.setWriteTimestampToKafka(true);
-{% endhighlight %}
+**Note**: By default, the number of retries is set to "0". This means that when `setLogFailuresOnly` is set to `false`,
+the producer fails immediately on errors, including leader changes. The value is set to "0" by default to avoid
+duplicate messages in the target topic that are caused by retries. For most production environments with frequent broker changes,
+we recommend setting the number of retries to a higher value.
+**Note**: There is currently no transactional producer for Kafka, so Flink can not guarantee exactly-once delivery
+into a Kafka topic.
+
+
-## Kafka Connector metrics
+## Kafka Connector Metrics
Flink's Kafka connectors provide some metrics through Flink's [metrics system]({{ site.baseurl }}/monitoring/metrics.html) to analyze
the behavior of the connector.
-The producers export Kafka's internal metrics through Flink's metric system for all supported versions. The consumers export
-all metrics starting from Kafka version 0.10. The Kafka documentation lists all exported metrics
-in its [documentation](http://kafka.apache.org/documentation/#selector_monitoring).
+The producers export Kafka's internal metrics through Flink's metric system for all supported versions.
+The Kafka documentation lists all exported metrics in its [documentation](http://kafka.apache.org/documentation/#selector_monitoring).
In addition to these metrics, all consumers expose the `current-offsets` and `committed-offsets` for each topic partition.
The `current-offsets` refers to the current offset in the partition. This refers to the offset of the last element that
we retrieved and emitted successfully. The `committed-offsets` is the last committed offset.
-The Kafka Consumers in Flink commit the offsets back to the Kafka brokers (Kafka 0.10+). If checkpointing
-is disabled, offsets are committed periodically.
+The Kafka Consumers in Flink commit the offsets back to the Kafka brokers.
+If checkpointing is disabled, offsets are committed periodically.
With checkpointing, the commit happens once all operators in the streaming topology have confirmed that they've created a checkpoint of their state.
This provides users with at-least-once semantics for the offsets committed to Zookeeper or the broker. For offsets checkpointed to Flink, the system
provides exactly once guarantees.
@@ -776,6 +605,15 @@ A mismatch in service name between client and server configuration will cause th
For more information on Flink configuration for Kerberos security, please see [here]({{ site.baseurl}}/ops/config.html).
You can also find [here]({{ site.baseurl}}/ops/security-kerberos.html) further details on how Flink internally setups Kerberos-based security.
+## Migrating Kafka Connector from 0.11 to universal
+
+In order to perform the migration, see the [upgrading jobs and Flink versions guide]({{ site.baseurl }}/ops/upgrading.html)
+and:
+* Use Flink 1.9 or newer for the whole process.
+* Do not upgrade Flink and user operators at the same time.
+* Make sure that Kafka Consumer and/or Kafka Producer used in your job have assigned unique identifiers (`uid`):
+* Use stop with savepoint feature to take the savepoint (for example by using `stop --withSavepoint`)[CLI command]({{ site.baseurl }}/ops/cli.html).
+
## Troubleshooting
diff --git a/docs/dev/connectors/kafka.zh.md b/docs/dev/connectors/kafka.zh.md
index b2cdc54ec56c04..d2686a2b796474 100644
--- a/docs/dev/connectors/kafka.zh.md
+++ b/docs/dev/connectors/kafka.zh.md
@@ -84,7 +84,7 @@ Flink 提供了专门的 Kafka 连接器,向 Kafka topic 中读取或者写入
{% endhighlight %}
请注意:目前流连接器还不是二进制分发的一部分。
-[在此处]({{ site.baseurl }}/zh/dev/projectsetup/dependencies.html)可以了解到如何链接它们以实现在集群中执行。
+[在此处]({{ site.baseurl }}/zh/dev/project-configuration.html)可以了解到如何链接它们以实现在集群中执行。
## 安装 Apache Kafka
diff --git a/docs/dev/connectors/kinesis.zh.md b/docs/dev/connectors/kinesis.zh.md
index 4e9009da5ee22e..9601af298d1585 100644
--- a/docs/dev/connectors/kinesis.zh.md
+++ b/docs/dev/connectors/kinesis.zh.md
@@ -45,7 +45,25 @@ Due to the licensing issue, the `flink-connector-kinesis{{ site.scala_version_su
## Using the Amazon Kinesis Streams Service
Follow the instructions from the [Amazon Kinesis Streams Developer Guide](https://docs.aws.amazon.com/streams/latest/dev/learning-kinesis-module-one-create-stream.html)
-to setup Kinesis streams. Make sure to create the appropriate IAM policy and user to read / write to the Kinesis streams.
+to setup Kinesis streams.
+
+## Configuring Access to Kinesis with IAM
+Make sure to create the appropriate IAM policy to allow reading / writing to / from the Kinesis streams. See examples [here](https://docs.aws.amazon.com/streams/latest/dev/controlling-access.html).
+
+Depending on your deployment you would choose a different Credentials Provider to allow access to Kinesis.
+By default, the `AUTO` Credentials Provider is used.
+If the access key ID and secret key are set in the configuration, the `BASIC` provider is used.
+
+A specific Credentials Provider can **optionally** be set by using the `AWSConfigConstants.AWS_CREDENTIALS_PROVIDER` setting.
+
+Supported Credential Providers are:
+* `AUTO` - Using the default AWS Credentials Provider chain that searches for credentials in the following order: `ENV_VARS`, `SYS_PROPS`, `WEB_IDENTITY_TOKEN`, `PROFILE` and EC2/ECS credentials provider.
+* `BASIC` - Using access key ID and secret key supplied as configuration.
+* `ENV_VAR` - Using `AWS_ACCESS_KEY_ID` & `AWS_SECRET_ACCESS_KEY` environment variables.
+* `SYS_PROP` - Using Java system properties aws.accessKeyId and aws.secretKey.
+* `PROFILE` - Use AWS credentials profile file to create the AWS credentials.
+* `ASSUME_ROLE` - Create AWS credentials by assuming a role. The credentials for assuming the role must be supplied.
+* `WEB_IDENTITY_TOKEN` - Create AWS credentials by assuming a role using Web Identity Token.
## Kinesis Consumer
@@ -91,8 +109,7 @@ The above is a simple example of using the consumer. Configuration for the consu
instance, the configuration keys for which can be found in `AWSConfigConstants` (AWS-specific parameters) and
`ConsumerConfigConstants` (Kinesis consumer parameters). The example
demonstrates consuming a single Kinesis stream in the AWS region "us-east-1". The AWS credentials are supplied using the basic method in which
-the AWS access key ID and secret access key are directly supplied in the configuration (other options are setting
-`AWSConfigConstants.AWS_CREDENTIALS_PROVIDER` to `ENV_VAR`, `SYS_PROP`, `PROFILE`, `ASSUME_ROLE`, and `AUTO`). Also, data is being consumed
+the AWS access key ID and secret access key are directly supplied in the configuration. Also, data is being consumed
from the newest position in the Kinesis stream (the other option will be setting `ConsumerConfigConstants.STREAM_INITIAL_POSITION`
to `TRIM_HORIZON`, which lets the consumer start reading the Kinesis stream from the earliest record possible).
diff --git a/docs/dev/connectors/nifi.md b/docs/dev/connectors/nifi.md
index 97fd831b6a6baa..7738a40eb432b4 100644
--- a/docs/dev/connectors/nifi.md
+++ b/docs/dev/connectors/nifi.md
@@ -37,7 +37,7 @@ following dependency to your project:
Note that the streaming connectors are currently not part of the binary
distribution. See
-[here]({{site.baseurl}}/dev/projectsetup/dependencies.html)
+[here]({{site.baseurl}}/dev/project-configuration.html)
for information about how to package the program with the libraries for
cluster execution.
diff --git a/docs/dev/connectors/nifi.zh.md b/docs/dev/connectors/nifi.zh.md
index 114092f9a3306b..c34c146750687b 100644
--- a/docs/dev/connectors/nifi.zh.md
+++ b/docs/dev/connectors/nifi.zh.md
@@ -34,7 +34,7 @@ under the License.
{% endhighlight %}
-注意这些连接器目前还没有包含在二进制发行版中。添加依赖、打包配置以及集群运行的相关信息请参考 [这里]({{site.baseurl}}/zh/dev/projectsetup/dependencies.html)。
+注意这些连接器目前还没有包含在二进制发行版中。添加依赖、打包配置以及集群运行的相关信息请参考 [这里]({{site.baseurl}}/zh/dev/project-configuration.html)。
#### 安装 Apache NiFi
diff --git a/docs/dev/connectors/pubsub.md b/docs/dev/connectors/pubsub.md
index 6055d9f67ae90e..c464f48838c5ca 100644
--- a/docs/dev/connectors/pubsub.md
+++ b/docs/dev/connectors/pubsub.md
@@ -41,7 +41,7 @@ following dependency to your project:
Note that the streaming connectors are currently not part of the binary
distribution. See
-[here]({{ site.baseurl }}/dev/projectsetup/dependencies.html)
+[here]({{ site.baseurl }}/dev/project-configuration.html)
for information about how to package the program with the libraries for
cluster execution.
diff --git a/docs/dev/connectors/pubsub.zh.md b/docs/dev/connectors/pubsub.zh.md
index eaf5f582c8467c..6fb9bd54f7e3b8 100644
--- a/docs/dev/connectors/pubsub.zh.md
+++ b/docs/dev/connectors/pubsub.zh.md
@@ -37,7 +37,7 @@ under the License.
注意:此连接器最近才加到 Flink 里,还未接受广泛测试。
-注意连接器目前还不是二进制发行版的一部分,添加依赖、打包配置以及集群运行信息请参考[这里]({{ site.baseurl }}/zh/dev/projectsetup/dependencies.html)
+注意连接器目前还不是二进制发行版的一部分,添加依赖、打包配置以及集群运行信息请参考[这里]({{ site.baseurl }}/zh/dev/project-configuration.html)
## Consuming or Producing PubSubMessages
diff --git a/docs/dev/connectors/rabbitmq.md b/docs/dev/connectors/rabbitmq.md
index a3a56a8349ed87..819979ed9119b6 100644
--- a/docs/dev/connectors/rabbitmq.md
+++ b/docs/dev/connectors/rabbitmq.md
@@ -47,7 +47,7 @@ This connector provides access to data streams from [RabbitMQ](http://www.rabbit
{% endhighlight %}
-Note that the streaming connectors are currently not part of the binary distribution. See linking with them for cluster execution [here]({{site.baseurl}}/dev/projectsetup/dependencies.html).
+Note that the streaming connectors are currently not part of the binary distribution. See linking with them for cluster execution [here]({{site.baseurl}}/dev/project-configuration.html).
### Installing RabbitMQ
Follow the instructions from the [RabbitMQ download page](http://www.rabbitmq.com/download.html). After the installation the server automatically starts, and the application connecting to RabbitMQ can be launched.
diff --git a/docs/dev/connectors/rabbitmq.zh.md b/docs/dev/connectors/rabbitmq.zh.md
index e213d3f99b19a9..b9889f563a77e5 100644
--- a/docs/dev/connectors/rabbitmq.zh.md
+++ b/docs/dev/connectors/rabbitmq.zh.md
@@ -43,7 +43,7 @@ Flink 自身既没有复用 "RabbitMQ AMQP Java Client" 的代码,也没有将
{% endhighlight %}
-注意连接器现在没有包含在二进制发行版中。集群执行的相关信息请参考 [这里]({{site.baseurl}}/zh/dev/projectsetup/dependencies.html).
+注意连接器现在没有包含在二进制发行版中。集群执行的相关信息请参考 [这里]({{site.baseurl}}/zh/dev/project-configuration.html).
### 安装 RabbitMQ
安装 RabbitMQ 请参考 [RabbitMQ 下载页面](http://www.rabbitmq.com/download.html)。安装完成之后,服务会自动拉起,应用程序就可以尝试连接到 RabbitMQ 了。
diff --git a/docs/dev/connectors/streamfile_sink.md b/docs/dev/connectors/streamfile_sink.md
index 271019a8480a6b..121a02d6822c2f 100644
--- a/docs/dev/connectors/streamfile_sink.md
+++ b/docs/dev/connectors/streamfile_sink.md
@@ -139,6 +139,7 @@ are finalized for further encoding purposes.
Flink comes with four built-in BulkWriter factories:
- [ParquetWriterFactory]({{ site.javadocs_baseurl }}/api/java/org/apache/flink/formats/parquet/ParquetWriterFactory.html)
+ - [AvroWriterFactory]({{ site.javadocs_baseurl }}/api/java/org/apache/flink/formats/avro/AvroWriterFactory.html)
- [SequenceFileWriterFactory]({{ site.javadocs_baseurl }}/api/java/org/apache/flink/formats/sequencefile/SequenceFileWriterFactory.html)
- [CompressWriterFactory]({{ site.javadocs_baseurl }}/api/java/org/apache/flink/formats/compress/CompressWriterFactory.html)
- [OrcBulkWriterFactory]({{ site.javadocs_baseurl }}/api/java/org/apache/flink/orc/writer/OrcBulkWriterFactory.html)
@@ -204,6 +205,106 @@ input.addSink(sink)
+#### Avro format
+
+Flink also provides built-in support for writing data into Avro files. A list of convenience methods to create
+Avro writer factories and their associated documentation can be found in the
+[AvroWriters]({{ site.javadocs_baseurl }}/api/java/org/apache/flink/formats/avro/AvroWriters.html) class.
+
+To use the Avro writers in your application you need to add the following dependency:
+
+{% highlight xml %}
+
+ org.apache.flink
+ flink-avro
+ {{ site.version }}
+
+{% endhighlight %}
+
+A StreamingFileSink that writes data to Avro files can be created like this:
+
+
+
+For creating customized Avro writers, e.g. enabling compression, users need to create the `AvroWriterFactory`
+with a custom implementation of the [AvroBuilder]({{ site.javadocs_baseurl }}/api/java/org/apache/flink/formats/avro/AvroBuilder.html) interface:
+
+
+
#### ORC Format
To enable the data to be bulk encoded in ORC format, Flink offers [OrcBulkWriterFactory]({{ site.javadocs_baseurl }}/api/java/org/apache/flink/formats/orc/writers/OrcBulkWriterFactory.html)
@@ -231,6 +332,7 @@ class Person {
{% endhighlight %}
+
Then a child implementation to convert the element of type `Person` and set them in the `VectorizedRowBatch` can be like:
@@ -631,6 +733,9 @@ Given this, when trying to restore from an old checkpoint/savepoint which assume
by subsequent successful checkpoints, Flink will refuse to resume and it will throw an exception as it cannot locate the
in-progress file.
+Important Note 4: Currently, the `StreamingFileSink` only supports three filesystems:
+HDFS, S3, and Local. Flink will throw an exception when using an unsupported filesystem at runtime.
+
### S3-specific
Important Note 1: For S3, the `StreamingFileSink`
diff --git a/docs/dev/connectors/streamfile_sink.zh.md b/docs/dev/connectors/streamfile_sink.zh.md
index bd74bef5290533..4e7fe46a1f7c8c 100644
--- a/docs/dev/connectors/streamfile_sink.zh.md
+++ b/docs/dev/connectors/streamfile_sink.zh.md
@@ -122,11 +122,13 @@ input.addSink(sink)
批量编码 Sink 的创建与行编码 Sink 相似,不过在这里我们不是指定编码器 `Encoder` 而是指定 [BulkWriter.Factory]({{ site.javadocs_baseurl }}/api/java/org/apache/flink/api/common/serialization/BulkWriter.Factory.html) 。
`BulkWriter` 定义了如何添加、刷新元素,以及如何批量编码。
-Flink 有三个内置的 BulkWriter Factory :
+Flink 有四个内置的 BulkWriter Factory :
- [ParquetWriterFactory]({{ site.javadocs_baseurl }}/api/java/org/apache/flink/formats/parquet/ParquetWriterFactory.html)
+ - [AvroWriterFactory]({{ site.javadocs_baseurl }}/api/java/org/apache/flink/formats/avro/AvroWriterFactory.html)
- [SequenceFileWriterFactory]({{ site.javadocs_baseurl }}/api/java/org/apache/flink/formats/sequencefile/SequenceFileWriterFactory.html)
- [CompressWriterFactory]({{ site.javadocs_baseurl }}/api/java/org/apache/flink/formats/compress/CompressWriterFactory.html)
+ - [OrcBulkWriterFactory]({{ site.javadocs_baseurl }}/api/java/org/apache/flink/orc/writer/OrcBulkWriterFactory.html)
+
+#### ORC Format
+
+To enable the data to be bulk encoded in ORC format, Flink offers [OrcBulkWriterFactory]({{ site.javadocs_baseurl }}/api/java/org/apache/flink/formats/orc/writers/OrcBulkWriterFactory.html)
+which takes a concrete implementation of [Vectorizer]({{ site.javadocs_baseurl }}/api/java/org/apache/flink/orc/vector/Vectorizer.html).
+
+Like any other columnar format that encodes data in bulk fashion, Flink's `OrcBulkWriter` writes the input elements in batches. It uses
+ORC's `VectorizedRowBatch` to achieve this.
+
+Since the input element has to be transformed to a `VectorizedRowBatch`, users have to extend the abstract `Vectorizer`
+class and override the `vectorize(T element, VectorizedRowBatch batch)` method. As you can see, the method provides an
+instance of `VectorizedRowBatch` to be used directly by the users so users just have to write the logic to transform the
+input `element` to `ColumnVectors` and set them in the provided `VectorizedRowBatch` instance.
+
+For example, if the input element is of type `Person` which looks like:
+
+
+
+{% highlight java %}
+
+class Person {
+ private final String name;
+ private final int age;
+ ...
+}
+
+{% endhighlight %}
+
+
+
+Then a child implementation to convert the element of type `Person` and set them in the `VectorizedRowBatch` can be like:
+
+
+
+To use the ORC bulk encoder in an application, users need to add the following dependency:
+
+{% highlight xml %}
+
+ org.apache.flink
+ flink-orc{{ site.scala_version_suffix }}
+ {{ site.version }}
+
+{% endhighlight %}
+
+And then a `StreamingFileSink` that writes data in ORC format can be created like this:
+
+
+
+OrcBulkWriterFactory can also take Hadoop `Configuration` and `Properties` so that a custom Hadoop configuration and ORC
+writer properties can be provided.
+
+
+
+{% highlight java %}
+String schema = ...;
+Configuration conf = ...;
+Properties writerProperties = new Properties();
+
+writerProps.setProperty("orc.compress", "LZ4");
+// Other ORC supported properties can also be set similarly.
+
+final OrcBulkWriterFactory writerFactory = new OrcBulkWriterFactory<>(
+ new PersonVectorizer(schema), writerProperties, conf);
+
+{% endhighlight %}
+
+
+{% highlight scala %}
+val schema: String = ...
+val conf: Configuration = ...
+val writerProperties: Properties = new Properties()
+
+writerProps.setProperty("orc.compress", "LZ4")
+// Other ORC supported properties can also be set similarly.
+
+val writerFactory = new OrcBulkWriterFactory(
+ new PersonVectorizer(schema), writerProperties, conf)
+{% endhighlight %}
+
+
+
+The complete list of ORC writer properties can be found [here](https://orc.apache.org/docs/hive-config.html).
+
+Users who want to add user metadata to the ORC files can do so by calling `addUserMetadata(...)` inside the overriding
+`vectorize(...)` method.
+
+
+
#### Hadoop SequenceFile 格式
在应用中使用 SequenceFile 批量编码器,你需要添加以下依赖:
@@ -405,6 +705,8 @@ Hadoop 2.7 之前的版本不支持这个方法,因此 Flink 会报异常。
重要提示 3: Flink 以及 `StreamingFileSink` 不会覆盖已经提交的数据。因此如果尝试从一个包含 in-progress 文件的旧 checkpoint/savepoint 恢复,
且这些 in-progress 文件会被接下来的成功 checkpoint 提交,Flink 会因为无法找到 in-progress 文件而抛异常,从而恢复失败。
+重要提示 4: 目前 `StreamingFileSink` 只支持三种文件系统: HDFS、S3和Local。如果配置了不支持的文件系统,在执行的时候 Flink 会抛出异常。
+
### S3 特有的注意事项
重要提示 1: 对于 S3,`StreamingFileSink` 只支持基于 [Hadoop](https://hadoop.apache.org/)
diff --git a/docs/dev/connectors/twitter.md b/docs/dev/connectors/twitter.md
index 9ca394b69d0c1a..d2e76d8209de9e 100644
--- a/docs/dev/connectors/twitter.md
+++ b/docs/dev/connectors/twitter.md
@@ -36,7 +36,7 @@ To use this connector, add the following dependency to your project:
{% endhighlight %}
Note that the streaming connectors are currently not part of the binary distribution.
-See linking with them for cluster execution [here]({{site.baseurl}}/dev/projectsetup/dependencies.html).
+See linking with them for cluster execution [here]({{site.baseurl}}/dev/project-configuration.html).
#### Authentication
In order to connect to the Twitter stream the user has to register their program and acquire the necessary information for the authentication. The process is described below.
diff --git a/docs/dev/connectors/twitter.zh.md b/docs/dev/connectors/twitter.zh.md
index f4d110d44c024a..ae09a7e6332b0f 100644
--- a/docs/dev/connectors/twitter.zh.md
+++ b/docs/dev/connectors/twitter.zh.md
@@ -35,7 +35,7 @@ Flink Streaming 通过一个内置的 `TwitterSource` 类来创建到 tweets 流
{% endhighlight %}
-注意:当前的二进制发行版还没有这些连接器。集群执行请参考[这里]({{site.baseurl}}/zh/dev/projectsetup/dependencies.html).
+注意:当前的二进制发行版还没有这些连接器。集群执行请参考[这里]({{site.baseurl}}/zh/dev/project-configuration.html).
#### 认证
使用 Twitter 流,用户需要先注册自己的程序,获取认证相关的必要信息。过程如下:
diff --git a/docs/dev/datastream_api.zh.md b/docs/dev/datastream_api.zh.md
index c95a9101dcd010..8300827cfa7dd4 100644
--- a/docs/dev/datastream_api.zh.md
+++ b/docs/dev/datastream_api.zh.md
@@ -32,19 +32,221 @@ example write the data to files, or to standard output (for example the command
terminal). Flink programs run in a variety of contexts, standalone, or embedded in other programs.
The execution can happen in a local JVM, or on clusters of many machines.
-Please see [basic concepts]({{ site.baseurl }}/dev/api_concepts.html) for an introduction
-to the basic concepts of the Flink API.
-
-In order to create your own Flink DataStream program, we encourage you to start with
-[anatomy of a Flink Program]({{ site.baseurl }}/dev/api_concepts.html#anatomy-of-a-flink-program)
-and gradually add your own
-[stream transformations]({{ site.baseurl }}/dev/stream/operators/index.html). The remaining sections act as references for additional
-operations and advanced features.
+In order to create your own Flink DataStream program, we encourage you to start
+with [anatomy of a Flink Program](#anatomy-of-a-flink-program) and gradually
+add your own [stream transformations]({{ site.baseurl
+}}/dev/stream/operators/index.html). The remaining sections act as references
+for additional operations and advanced features.
* This will be replaced by the TOC
{:toc}
+What is a DataStream?
+----------------------
+
+The DataStream API gets its name from the special `DataStream` class that is
+used to represent a collection of data in a Flink program. You can think of
+them as immutable collections of data that can contain duplicates. This data
+can either be finite or unbounded, the API that you use to work on them is the
+same.
+
+A `DataStream` is similar to a regular Java `Collection` in terms of usage but
+is quite different in some key ways. They are immutable, meaning that once they
+are created you cannot add or remove elements. You can also not simply inspect
+the elements inside but only work on them using the `DataStream` API
+operations, which are also called transformations.
+
+You can create an initial `DataStream` by adding a source in a Flink program.
+Then you can derive new streams from this and combine them by using API methods
+such as `map`, `filter`, and so on.
+
+Anatomy of a Flink Program
+--------------------------
+
+Flink programs look like regular programs that transform `DataStreams`. Each
+program consists of the same basic parts:
+
+1. Obtain an `execution environment`,
+2. Load/create the initial data,
+3. Specify transformations on this data,
+4. Specify where to put the results of your computations,
+5. Trigger the program execution
+
+
+
+
+
+
+We will now give an overview of each of those steps, please refer to the
+respective sections for more details. Note that all core classes of the Java
+DataStream API can be found in {% gh_link
+/flink-streaming-java/src/main/java/org/apache/flink/streaming/api
+"org.apache.flink.streaming.api" %}.
+
+The `StreamExecutionEnvironment` is the basis for all Flink programs. You can
+obtain one using these static methods on `StreamExecutionEnvironment`:
+
+{% highlight java %}
+getExecutionEnvironment()
+
+createLocalEnvironment()
+
+createRemoteEnvironment(String host, int port, String... jarFiles)
+{% endhighlight %}
+
+Typically, you only need to use `getExecutionEnvironment()`, since this will do
+the right thing depending on the context: if you are executing your program
+inside an IDE or as a regular Java program it will create a local environment
+that will execute your program on your local machine. If you created a JAR file
+from your program, and invoke it through the [command line]({{ site.baseurl
+}}/ops/cli.html), the Flink cluster manager will execute your main method and
+`getExecutionEnvironment()` will return an execution environment for executing
+your program on a cluster.
+
+For specifying data sources the execution environment has several methods to
+read from files using various methods: you can just read them line by line, as
+CSV files, or using any of the other provided sources. To just read a text file
+as a sequence of lines, you can use:
+
+{% highlight java %}
+final StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
+
+DataStream text = env.readTextFile("file:///path/to/file");
+{% endhighlight %}
+
+This will give you a DataStream on which you can then apply transformations to create new
+derived DataStreams.
+
+You apply transformations by calling methods on DataStream with a
+transformation functions. For example, a map transformation looks like this:
+
+{% highlight java %}
+DataStream input = ...;
+
+DataStream parsed = input.map(new MapFunction() {
+ @Override
+ public Integer map(String value) {
+ return Integer.parseInt(value);
+ }
+});
+{% endhighlight %}
+
+This will create a new DataStream by converting every String in the original
+collection to an Integer.
+
+Once you have a DataStream containing your final results, you can write it to
+an outside system by creating a sink. These are just some example methods for
+creating a sink:
+
+{% highlight java %}
+writeAsText(String path)
+
+print()
+{% endhighlight %}
+
+
+
+
+We will now give an overview of each of those steps, please refer to the
+respective sections for more details. Note that all core classes of the Scala
+DataStream API can be found in {% gh_link
+/flink-streaming-scala/src/main/scala/org/apache/flink/streaming/api/scala
+"org.apache.flink.streaming.api.scala" %}.
+
+The `StreamExecutionEnvironment` is the basis for all Flink programs. You can
+obtain one using these static methods on `StreamExecutionEnvironment`:
+
+{% highlight scala %}
+getExecutionEnvironment()
+
+createLocalEnvironment()
+
+createRemoteEnvironment(host: String, port: Int, jarFiles: String*)
+{% endhighlight %}
+
+Typically, you only need to use `getExecutionEnvironment()`, since this will do
+the right thing depending on the context: if you are executing your program
+inside an IDE or as a regular Java program it will create a local environment
+that will execute your program on your local machine. If you created a JAR file
+from your program, and invoke it through the [command line]({{ site.baseurl
+}}/ops/cli.html), the Flink cluster manager will execute your main method and
+`getExecutionEnvironment()` will return an execution environment for executing
+your program on a cluster.
+
+For specifying data sources the execution environment has several methods to
+read from files using various methods: you can just read them line by line, as
+CSV files, or using any of the other provided sources. To just read a text file
+as a sequence of lines, you can use:
+
+{% highlight scala %}
+val env = StreamExecutionEnvironment.getExecutionEnvironment()
+
+val text: DataStream[String] = env.readTextFile("file:///path/to/file")
+{% endhighlight %}
+
+This will give you a DataStream on which you can then apply transformations to
+create new derived DataStreams.
+
+You apply transformations by calling methods on DataStream with a
+transformation functions. For example, a map transformation looks like this:
+
+{% highlight scala %}
+val input: DataSet[String] = ...
+
+val mapped = input.map { x => x.toInt }
+{% endhighlight %}
+
+This will create a new DataStream by converting every String in the original
+collection to an Integer.
+
+Once you have a DataStream containing your final results, you can write it to
+an outside system by creating a sink. These are just some example methods for
+creating a sink:
+
+{% highlight scala %}
+writeAsText(path: String)
+
+print()
+{% endhighlight %}
+
+
+
+
+Once you specified the complete program you need to **trigger the program
+execution** by calling `execute()` on the `StreamExecutionEnvironment`.
+Depending on the type of the `ExecutionEnvironment` the execution will be
+triggered on your local machine or submit your program for execution on a
+cluster.
+
+The `execute()` method will wait for the job to finish and then return a
+`JobExecutionResult`, this contains execution times and accumulator results.
+
+If you don't want to wait for the job to finish, you can trigger asynchronous
+job execution by calling `executeAysnc()` on the `StreamExecutionEnvironment`.
+It will return a `JobClient` with which you can communicate with the job you
+just submitted. For instance, here is how to implement the semantics of
+`execute()` by using `executeAsync()`.
+
+{% highlight java %}
+final JobClient jobClient = env.executeAsync();
+
+final JobExecutionResult jobExecutionResult = jobClient.getJobExecutionResult(userClassloader).get();
+{% endhighlight %}
+
+That last part about program execution is crucial to understanding when and how
+Flink operations are executed. All Flink programs are executed lazily: When the
+program's main method is executed, the data loading and transformations do not
+happen directly. Rather, each operation is created and added to a dataflow
+graph. The operations are actually executed when the execution is explicitly
+triggered by an `execute()` call on the execution environment. Whether the
+program is executed locally or on a cluster depends on the type of execution
+environment
+
+The lazy evaluation lets you construct sophisticated programs that Flink
+executes as one holistically planned unit.
+
+{% top %}
Example Program
---------------
diff --git a/docs/dev/event_time.md b/docs/dev/event_time.md
index a25122f1bdee3a..3e2573496ff51f 100644
--- a/docs/dev/event_time.md
+++ b/docs/dev/event_time.md
@@ -34,30 +34,22 @@ For information about how to use time in Flink programs refer to
[ProcessFunction]({% link
dev/stream/operators/process_function.md %}).
-* toc
-{:toc}
+A prerequisite for using *event time* processing is setting the right *time
+characteristic*. That setting defines how data stream sources behave (for
+example, whether they will assign timestamps), and what notion of time should
+be used by window operations like `KeyedStream.timeWindow(Time.seconds(30))`.
-## Setting a Time Characteristic
-
-The first part of a Flink DataStream program usually sets the base *time characteristic*. That setting
-defines how data stream sources behave (for example, whether they will assign timestamps), and what notion of
-time should be used by window operations like `KeyedStream.timeWindow(Time.seconds(30))`.
-
-The following example shows a Flink program that aggregates events in hourly time windows. The behavior of the
-windows adapts with the time characteristic.
+You can set the time characteristic using
+`StreamExecutionEnvironment.setStreamTimeCharacteristic()`:
-Note that in order to run this example in *event time*, the program needs to either use sources
-that directly define event time for the data and emit watermarks themselves, or the program must
-inject a *Timestamp Assigner & Watermark Generator* after the sources. Those functions describe how to access
-the event timestamps, and what degree of out-of-orderness the event stream exhibits.
-
-The section below describes the general mechanism behind *timestamps* and *watermarks*. For a guide on how
-to use timestamp assignment and watermark generation in the Flink DataStream API, please refer to
-[Generating Timestamps / Watermarks]({{ site.baseurl }}/dev/event_timestamps_watermarks.html).
-
-{% top %}
-
-## Idling sources
-
-Currently, with pure event time watermarks generators, watermarks can not progress if there are no elements
-to be processed. That means in case of gap in the incoming data, event time will not progress and for
-example the window operator will not be triggered and thus existing windows will not be able to produce any
-output data.
-
-To circumvent this one can use periodic watermark assigners that don't only assign based on
-element timestamps. An example solution could be an assigner that switches to using current processing time
-as the time basis after not observing new events for a while.
-
-Sources can be marked as idle using `SourceFunction.SourceContext#markAsTemporarilyIdle`. For details please refer to the Javadoc of
-this method as well as `StreamStatus`.
-
-## Debugging Watermarks
-
-Please refer to the [Debugging Windows & Event Time]({{ site.baseurl }}/monitoring/debugging_event_time.html) section for debugging
-watermarks at runtime.
-
-## How operators are processing watermarks
-
-As a general rule, operators are required to completely process a given watermark before forwarding it downstream. For example,
-`WindowOperator` will first evaluate which windows should be fired, and only after producing all of the output triggered by
-the watermark will the watermark itself be sent downstream. In other words, all elements produced due to occurrence of a watermark
-will be emitted before the watermark.
-
-The same rule applies to `TwoInputStreamOperator`. However, in this case the current watermark of the operator is defined as
-the minimum of both of its inputs.
-
-The details of this behavior are defined by the implementations of the `OneInputStreamOperator#processWatermark`,
-`TwoInputStreamOperator#processWatermark1` and `TwoInputStreamOperator#processWatermark2` methods.
+Note that in order to run this example in *event time*, the program needs to
+either use sources that directly define event time for the data and emit
+watermarks themselves, or the program must inject a *Timestamp Assigner &
+Watermark Generator* after the sources. Those functions describe how to access
+the event timestamps, and what degree of out-of-orderness the event stream
+exhibits.
+
+## Where to go next?
+
+* [Generating Watermarks]({% link dev/event_timestamps_watermarks.md
+ %}): Shows how to write timestamp assigners and watermark generators, which
+ are needed for event-time aware Flink applications.
+* [Builtin Watermark Generators]({% link dev/event_timestamp_extractors.md %}):
+ Gives an overview of the builtin watermark generators.
+* [Debugging Windows & Event Time]({{ site.baseurl
+ }}/monitoring/debugging_event_time.html): Show how to debug problems around
+ watermarks and timestamps in event-time Flink applications.
{% top %}
diff --git a/docs/dev/event_time.zh.md b/docs/dev/event_time.zh.md
index dbe964fee2fb9c..50c8560d6dbe53 100644
--- a/docs/dev/event_time.zh.md
+++ b/docs/dev/event_time.zh.md
@@ -89,11 +89,11 @@ stream
{% highlight python %}
env = StreamExecutionEnvironment.get_execution_environment()
-env.set_stream_time_characteristic(TimeCharacteristic.ProcessingTime)
+env.set_stream_time_characteristic(TimeCharacteristic.EventTime)
# alternatively:
# env.set_stream_time_characteristic(TimeCharacteristic.IngestionTime)
-# env.set_stream_time_characteristic(TimeCharacteristic.EventTime)
+# env.set_stream_time_characteristic(TimeCharacteristic.ProcessingTime)
{% endhighlight %}
diff --git a/docs/dev/event_timestamp_extractors.md b/docs/dev/event_timestamp_extractors.md
index 01b3634867edf3..417658c22c5664 100644
--- a/docs/dev/event_timestamp_extractors.md
+++ b/docs/dev/event_timestamp_extractors.md
@@ -1,5 +1,5 @@
---
-title: "Pre-defined Timestamp Extractors / Watermark Emitters"
+title: "Builtin Watermark Generators"
nav-parent_id: event_time
nav-pos: 2
---
@@ -25,83 +25,73 @@ under the License.
* toc
{:toc}
-As described in [timestamps and watermark handling]({{ site.baseurl }}/dev/event_timestamps_watermarks.html),
-Flink provides abstractions that allow the programmer to assign their own timestamps and emit their own watermarks. More specifically,
-one can do so by implementing one of the `AssignerWithPeriodicWatermarks` and `AssignerWithPunctuatedWatermarks` interfaces, depending
-on the use case. In a nutshell, the first will emit watermarks periodically, while the second does so based on some property of
-the incoming records, e.g. whenever a special element is encountered in the stream.
+As described in [Generating Watermarks]({{ site.baseurl
+}}/dev/event_timestamps_watermarks.html), Flink provides abstractions that
+allow the programmer to assign their own timestamps and emit their own
+watermarks. More specifically, one can do so by implementing the
+`WatermarkGenerator` interface.
-In order to further ease the programming effort for such tasks, Flink comes with some pre-implemented timestamp assigners.
-This section provides a list of them. Apart from their out-of-the-box functionality, their implementation can serve as an example
-for custom implementations.
+In order to further ease the programming effort for such tasks, Flink comes
+with some pre-implemented timestamp assigners. This section provides a list of
+them. Apart from their out-of-the-box functionality, their implementation can
+serve as an example for custom implementations.
-### **Assigners with ascending timestamps**
+## Monotonously Increasing Timestamps
-The simplest special case for *periodic* watermark generation is the case where timestamps seen by a given source task
-occur in ascending order. In that case, the current timestamp can always act as a watermark, because no earlier timestamps will
-arrive.
+The simplest special case for *periodic* watermark generation is the when
+timestamps seen by a given source task occur in ascending order. In that case,
+the current timestamp can always act as a watermark, because no earlier
+timestamps will arrive.
-Note that it is only necessary that timestamps are ascending *per parallel data source task*. For example, if
-in a specific setup one Kafka partition is read by one parallel data source instance, then it is only necessary that
-timestamps are ascending within each Kafka partition. Flink's watermark merging mechanism will generate correct
-watermarks whenever parallel streams are shuffled, unioned, connected, or merged.
+Note that it is only necessary that timestamps are ascending *per parallel data
+source task*. For example, if in a specific setup one Kafka partition is read
+by one parallel data source instance, then it is only necessary that timestamps
+are ascending within each Kafka partition. Flink's watermark merging mechanism
+will generate correct watermarks whenever parallel streams are shuffled,
+unioned, connected, or merged.
-### **Assigners allowing a fixed amount of lateness**
-
-Another example of periodic watermark generation is when the watermark lags behind the maximum (event-time) timestamp
-seen in the stream by a fixed amount of time. This case covers scenarios where the maximum lateness that can be encountered in a
-stream is known in advance, e.g. when creating a custom source containing elements with timestamps spread within a fixed period of
-time for testing. For these cases, Flink provides the `BoundedOutOfOrdernessTimestampExtractor` which takes as an argument
-the `maxOutOfOrderness`, i.e. the maximum amount of time an element is allowed to be late before being ignored when computing the
-final result for the given window. Lateness corresponds to the result of `t - t_w`, where `t` is the (event-time) timestamp of an
-element, and `t_w` that of the previous watermark. If `lateness > 0` then the element is considered late and is, by default, ignored when computing
-the result of the job for its corresponding window. See the documentation about [allowed lateness]({{ site.baseurl }}/dev/stream/operators/windows.html#allowed-lateness)
-for more information about working with late elements.
+## Fixed Amount of Lateness
+
+Another example of periodic watermark generation is when the watermark lags
+behind the maximum (event-time) timestamp seen in the stream by a fixed amount
+of time. This case covers scenarios where the maximum lateness that can be
+encountered in a stream is known in advance, e.g. when creating a custom source
+containing elements with timestamps spread within a fixed period of time for
+testing. For these cases, Flink provides the `BoundedOutOfOrdernessWatermarks`
+generator which takes as an argument the `maxOutOfOrderness`, i.e. the maximum
+amount of time an element is allowed to be late before being ignored when
+computing the final result for the given window. Lateness corresponds to the
+result of `t - t_w`, where `t` is the (event-time) timestamp of an element, and
+`t_w` that of the previous watermark. If `lateness > 0` then the element is
+considered late and is, by default, ignored when computing the result of the
+job for its corresponding window. See the documentation about [allowed
+lateness]({{ site.baseurl
+}}/dev/stream/operators/windows.html#allowed-lateness) for more information
+about working with late elements.
diff --git a/docs/dev/event_timestamps_watermarks.md b/docs/dev/event_timestamps_watermarks.md
index 28e1f2f343d1e8..3b8d7661eedb09 100644
--- a/docs/dev/event_timestamps_watermarks.md
+++ b/docs/dev/event_timestamps_watermarks.md
@@ -1,5 +1,5 @@
---
-title: "Generating Timestamps / Watermarks"
+title: "Generating Watermarks"
nav-parent_id: event_time
nav-pos: 1
---
@@ -22,115 +22,106 @@ specific language governing permissions and limitations
under the License.
-->
+In this section you will learn about the APIs that Flink provides for working
+with **event time** timestamps and watermarks. For an introduction to *event
+time*, *processing time*, and *ingestion time*, please refer to the
+[introduction to event time]({{ site.baseurl }}/dev/event_time.html).
+
* toc
{:toc}
+## Introduction to Watermark Strategies
-This section is relevant for programs running on **event time**. For an introduction to *event time*,
-*processing time*, and *ingestion time*, please refer to the [introduction to event time]({{ site.baseurl }}/dev/event_time.html).
-
-To work with *event time*, streaming programs need to set the *time characteristic* accordingly.
-
-
-
-## Assigning Timestamps
-
-In order to work with *event time*, Flink needs to know the events' *timestamps*, meaning each element in the
-stream needs to have its event timestamp *assigned*. This is usually done by accessing/extracting the
-timestamp from some field in the element.
+In order to work with *event time*, Flink needs to know the events
+*timestamps*, meaning each element in the stream needs to have its event
+timestamp *assigned*. This is usually done by accessing/extracting the
+timestamp from some field in the element by using a `TimestampAssigner`.
-Timestamp assignment goes hand-in-hand with generating watermarks, which tell the system about
-progress in event time.
+Timestamp assignment goes hand-in-hand with generating watermarks, which tell
+the system about progress in event time. You can configure this by specifying a
+`WatermarkGenerator`.
-There are two ways to assign timestamps and generate watermarks:
+The Flink API expects a `WatermarkStrategy` that contains both a
+`TimestampAssigner` and `WatermarkGenerator`. A number of common strategies
+are available out of the box as static methods on `WatermarkStrategy`, but
+users can also build their own strategies when required.
- 1. Directly in the data stream source
- 2. Via a timestamp assigner / watermark generator: in Flink, timestamp assigners also define the watermarks to be emitted
+Here is the interface for completeness' sake:
-Attention Both timestamps and watermarks are specified as
-milliseconds since the Java epoch of 1970-01-01T00:00:00Z.
-
-### Source Functions with Timestamps and Watermarks
+{% highlight java %}
+public interface WatermarkStrategy extends TimestampAssignerSupplier, WatermarkGeneratorSupplier{
-Stream sources can directly assign timestamps to the elements they produce, and they can also emit watermarks.
-When this is done, no timestamp assigner is needed.
-Note that if a timestamp assigner is used, any timestamps and watermarks provided by the source will be overwritten.
+ /**
+ * Instantiates a {@link TimestampAssigner} for assigning timestamps according to this
+ * strategy.
+ */
+ @Override
+ TimestampAssigner createTimestampAssigner(TimestampAssignerSupplier.Context context);
-To assign a timestamp to an element in the source directly, the source must use the `collectWithTimestamp(...)`
-method on the `SourceContext`. To generate watermarks, the source must call the `emitWatermark(Watermark)` function.
+ /**
+ * Instantiates a WatermarkGenerator that generates watermarks according to this strategy.
+ */
+ @Override
+ WatermarkGenerator createWatermarkGenerator(WatermarkGeneratorSupplier.Context context);
+}
+{% endhighlight %}
-Below is a simple example of a *(non-checkpointed)* source that assigns timestamps and generates watermarks:
+As mentioned, you usually don't implement this interface yourself but use the
+static helper methods on `WatermarkStrategy` for common watermark strategies or
+to bundle together a custom `TimestampAssigner` with a `WatermarkGenerator`.
+For example, to use bounded-out-of-orderness watermarks and a lambda function as a
+timestamp assigner you use this:
{% highlight scala %}
-override def run(ctx: SourceContext[MyType]): Unit = {
- while (/* condition */) {
- val next: MyType = getNext()
- ctx.collectWithTimestamp(next, next.eventTimestamp)
-
- if (next.hasWatermarkTime) {
- ctx.emitWatermark(new Watermark(next.getWatermarkTime))
- }
- }
-}
+WatermarkStrategy
+ .forBoundedOutOfOrderness[(Long, String)](Duration.ofSeconds(20))
+ .withTimestampAssigner(new SerializableTimestampAssigner[(Long, String)] {
+ override def extractTimestamp(element: (Long, String), recordTimestamp: Long): Long = element._1
+ })
{% endhighlight %}
+
+(Using Scala Lambdas here currently doesn't work because Scala is stupid and it's hard to support this. #fus)
+Specifying a `TimestampAssigner` is optional and in most cases you don't
+actually want to specify one. For example, when using Kafka or Kinesis you
+would get timestamps directly from the Kafka/Kinesis records.
+
+We will look at the `WatermarkGenerator` interface later in [Writing
+WatermarkGenerators](#writing-watermarkgenerators).
-### Timestamp Assigners / Watermark Generators
+
+Attention: Both timestamps and watermarks
+are specified as milliseconds since the Java epoch of 1970-01-01T00:00:00Z.
+
-Timestamp assigners take a stream and produce a new stream with timestamped elements and watermarks. If the
-original stream had timestamps and/or watermarks already, the timestamp assigner overwrites them.
+## Using Watermark Strategies
-Timestamp assigners are usually specified immediately after the data source, but it is not strictly required to do so.
-A common pattern, for example, is to parse (*MapFunction*) and filter (*FilterFunction*) before the timestamp assigner.
-In any case, the timestamp assigner needs to be specified before the first operation on event time
-(such as the first window operation). As a special case, when using Kafka as the source of a streaming job,
-Flink allows the specification of a timestamp assigner / watermark emitter inside
-the source (or consumer) itself. More information on how to do so can be found in the
-[Kafka Connector documentation]({{ site.baseurl }}/dev/connectors/kafka.html).
+There are two places in Flink applications where a `WatermarkStrategy` can be
+used: 1) directly on sources and 2) after non-source operation.
+The first option is preferable, because it allows sources to exploit knowledge
+about shards/partitions/splits in the watermarking logic. Sources can usually
+then track watermarks at a finer level and the overall watermark produced by a
+source will be more accurate. Specifying a `WatermarkStrategy` directly on the
+source usually means you have to use a source specific interface/ Refer to
+[Watermark Strategies and the Kafka
+Connector](#watermark-strategies-and-the-kafka-connector) for how this works on
+a Kafka Connector and for more details about how per-partition watermarking
+works there.
-**NOTE:** The remainder of this section presents the main interfaces a programmer has
-to implement in order to create her own timestamp extractors/watermark emitters.
-To see the pre-implemented extractors that ship with Flink, please refer to the
-[Pre-defined Timestamp Extractors / Watermark Emitters]({{ site.baseurl }}/dev/event_timestamp_extractors.html) page.
+The second option (setting a `WatermarkStrategy` after arbitrary operations)
+should only be used if you cannot set a strategy directly on the source:
+Using a `WatermarkStrategy` this way takes a stream and produce a new stream
+with timestamped elements and watermarks. If the original stream had timestamps
+and/or watermarks already, the timestamp assigner overwrites them.
+
+## Dealing With Idle Sources
+
+If one of the input splits/partitions/shards does not carry events for a while
+this means that the `WatermarkGenerator` also does not get any new information
+on which to base a watermark. We call this an *idle input* or an *idle source*.
+This is a problem because it can happen that some of your partitions do still
+carry events. In that case, the watermark will be held back, because it is
+computed as the minimum over all the different parallel watermarks.
+
+To deal with this, you can use a `WatermarkStrategy` that will detect idleness
+and mark an input as idle. `WatermarkStrategy` provides a convenience helper
+for this:
+
+
+
-#### **With Periodic Watermarks**
+## Writing WatermarkGenerators
-`AssignerWithPeriodicWatermarks` assigns timestamps and generates watermarks periodically (possibly depending
-on the stream elements, or purely based on processing time).
+A `TimestampAssigner` is a simple function that extracts a field from an event, we therefore don't need to look at them in detail. A `WatermarkGenerator`, on the other hand, is a bit more complicated to write and we will look at how you can do that in the next two sections. This is the `WatermarkGenerator` interface:
-The interval (every *n* milliseconds) in which the watermark will be generated is defined via
-`ExecutionConfig.setAutoWatermarkInterval(...)`. The assigner's `getCurrentWatermark()` method will be
-called each time, and a new watermark will be emitted if the returned watermark is non-null and larger than the previous
-watermark.
+{% highlight java %}
+/**
+ * The {@code WatermarkGenerator} generates watermarks either based on events or
+ * periodically (in a fixed interval).
+ *
+ *
Note: This WatermarkGenerator subsumes the previous distinction between the
+ * {@code AssignerWithPunctuatedWatermarks} and the {@code AssignerWithPeriodicWatermarks}.
+ */
+@Public
+public interface WatermarkGenerator {
+
+ /**
+ * Called for every event, allows the watermark generator to examine and remember the
+ * event timestamps, or to emit a watermark based on the event itself.
+ */
+ void onEvent(T event, long eventTimestamp, WatermarkOutput output);
+
+ /**
+ * Called periodically, and might emit a new watermark, or not.
+ *
+ *
The interval in which this method is called and Watermarks are generated
+ * depends on {@link ExecutionConfig#getAutoWatermarkInterval()}.
+ */
+ void onPeriodicEmit(WatermarkOutput output);
+}
+{% endhighlight %}
-Here we show two simple examples of timestamp assigners that use periodic watermark generation. Note that Flink ships with a `BoundedOutOfOrdernessTimestampExtractor` similar to the `BoundedOutOfOrdernessGenerator` shown below, which you can read about [here]({{ site.baseurl }}/dev/event_timestamp_extractors.html#assigners-allowing-a-fixed-amount-of-lateness).
+There are two different styles of watermark generation: *periodic* and
+*punctuated*.
+
+A periodic generator usually observes to the incoming events via `onEvent()`
+and then emits a watermark when the framework calls `onPeriodicEmit()`.
+
+A puncutated generator will look at events in `onEvent()` and wait for special
+*marker events* or *punctuations* that carry watermark information in the
+stream. When it sees one of these events it emits a watermark immediately.
+Usually, punctuated generators don't emit a watermark from `onPeriodicEmit()`.
+
+We will look at how to implement generators for each style next.
+
+### Writing a Periodic WatermarkGenerator
+
+A periodic generator observes stream events and generates
+watermarks periodically (possibly depending on the stream elements, or purely
+based on processing time).
+
+The interval (every *n* milliseconds) in which the watermark will be generated
+is defined via `ExecutionConfig.setAutoWatermarkInterval(...)`. The
+generators's `onPeriodicEmit()` method will be called each time, and a new
+watermark will be emitted if the returned watermark is non-null and larger than
+the previous watermark.
+
+Here we show two simple examples of watermark generators that use periodic
+watermark generation. Note that Flink ships with
+`BoundedOutOfOrdernessWatermarks`, which is a `WatermarkGenerator` that works
+similarly to the `BoundedOutOfOrdernessGenerator` shown below. You can read
+about using that [here]({{ site.baseurl
+}}/dev/event_timestamp_extractors.html#assigners-allowing-a-fixed-amount-of-lateness).
@@ -196,44 +272,42 @@ Here we show two simple examples of timestamp assigners that use periodic waterm
* but only to a certain degree. The latest elements for a certain timestamp t will arrive
* at most n milliseconds after the earliest elements for timestamp t.
*/
-public class BoundedOutOfOrdernessGenerator implements AssignerWithPeriodicWatermarks {
+public class BoundedOutOfOrdernessGenerator implements WatermarkGenerator {
private final long maxOutOfOrderness = 3500; // 3.5 seconds
private long currentMaxTimestamp;
@Override
- public long extractTimestamp(MyEvent element, long previousElementTimestamp) {
- long timestamp = element.getCreationTime();
- currentMaxTimestamp = Math.max(timestamp, currentMaxTimestamp);
- return timestamp;
+ public void onEvent(MyEvent event, long eventTimestamp, WatermarkOutput output) {
+ currentMaxTimestamp = Math.max(currentMaxTimestamp, eventTimestamp);
}
@Override
- public Watermark getCurrentWatermark() {
- // return the watermark as current highest timestamp minus the out-of-orderness bound
- return new Watermark(currentMaxTimestamp - maxOutOfOrderness);
+ public void onPeriodicEmit(WatermarkOutput output) {
+ // emit the watermark as current highest timestamp minus the out-of-orderness bound
+ output.emitWatermark(new Watermark(currentMaxTimestamp - maxOutOfOrderness - 1));
}
+
}
/**
* This generator generates watermarks that are lagging behind processing time by a fixed amount.
* It assumes that elements arrive in Flink after a bounded delay.
*/
-public class TimeLagWatermarkGenerator implements AssignerWithPeriodicWatermarks {
+public class TimeLagWatermarkGenerator implements WatermarkGenerator {
- private final long maxTimeLag = 5000; // 5 seconds
+ private final long maxTimeLag = 5000; // 5 seconds
- @Override
- public long extractTimestamp(MyEvent element, long previousElementTimestamp) {
- return element.getCreationTime();
- }
+ @Override
+ public void onEvent(MyEvent event, long eventTimestamp, WatermarkOutput output) {
+ // don't need to do anything because we work on processing time
+ }
- @Override
- public Watermark getCurrentWatermark() {
- // return the watermark as current time minus the maximum time lag
- return new Watermark(System.currentTimeMillis() - maxTimeLag);
- }
+ @Override
+ public void onPeriodicEmit(WatermarkOutput output) {
+ output.emitWatermark(new Watermark(System.currentTimeMillis() - maxTimeLag));
+ }
}
{% endhighlight %}
@@ -250,15 +324,13 @@ class BoundedOutOfOrdernessGenerator extends AssignerWithPeriodicWatermarks[MyEv
var currentMaxTimestamp: Long = _
- override def extractTimestamp(element: MyEvent, previousElementTimestamp: Long): Long = {
- val timestamp = element.getCreationTime()
- currentMaxTimestamp = max(timestamp, currentMaxTimestamp)
- timestamp
+ override def onEvent(element: MyEvent, eventTimestamp: Long): Unit = {
+ currentMaxTimestamp = max(eventTimestamp, currentMaxTimestamp)
}
- override def getCurrentWatermark(): Watermark = {
- // return the watermark as current highest timestamp minus the out-of-orderness bound
- new Watermark(currentMaxTimestamp - maxOutOfOrderness)
+ override def onPeriodicEmit(): Unit = {
+ // emit the watermark as current highest timestamp minus the out-of-orderness bound
+ output.emitWatermark(new Watermark(currentMaxTimestamp - maxOutOfOrderness - 1));
}
}
@@ -270,45 +342,43 @@ class TimeLagWatermarkGenerator extends AssignerWithPeriodicWatermarks[MyEvent]
val maxTimeLag = 5000L // 5 seconds
- override def extractTimestamp(element: MyEvent, previousElementTimestamp: Long): Long = {
- element.getCreationTime
+ override def onEvent(element: MyEvent, eventTimestamp: Long): Unit = {
+ // don't need to do anything because we work on processing time
}
- override def getCurrentWatermark(): Watermark = {
- // return the watermark as current time minus the maximum time lag
- new Watermark(System.currentTimeMillis() - maxTimeLag)
+ override def onPeriodicEmit(): Unit = {
+ output.emitWatermark(new Watermark(System.currentTimeMillis() - maxTimeLag));
}
}
{% endhighlight %}
-#### **With Punctuated Watermarks**
+### Writing a Punctuated WatermarkGenerator
-To generate watermarks whenever a certain event indicates that a new watermark might be generated, use
-`AssignerWithPunctuatedWatermarks`. For this class Flink will first call the `extractTimestamp(...)` method
-to assign the element a timestamp, and then immediately call the
-`checkAndGetNextWatermark(...)` method on that element.
+A punctuated watermark generator will observe the stream of
+events and emit a watermark whenever it sees a special element that carries
+watermark information.
-The `checkAndGetNextWatermark(...)` method is passed the timestamp that was assigned in the `extractTimestamp(...)`
-method, and can decide whether it wants to generate a watermark. Whenever the `checkAndGetNextWatermark(...)`
-method returns a non-null watermark, and that watermark is larger than the latest previous watermark, that
-new watermark will be emitted.
+This is how you can implement a punctuated generator that emits a watermark
+whenever an event indicates that it carries a certain marker:
{% highlight java %}
-public class PunctuatedAssigner implements AssignerWithPunctuatedWatermarks {
+public class PunctuatedAssigner implements WatermarkGenerator {
- @Override
- public long extractTimestamp(MyEvent element, long previousElementTimestamp) {
- return element.getCreationTime();
- }
+ @Override
+ public void onEvent(MyEvent event, long eventTimestamp, WatermarkOutput output) {
+ if (event.hasWatermarkMarker()) {
+ output.emitWatermark(new Watermark(event.getWatermarkTimestamp()));
+ }
+ }
- @Override
- public Watermark checkAndGetNextWatermark(MyEvent lastElement, long extractedTimestamp) {
- return lastElement.hasWatermarkMarker() ? new Watermark(extractedTimestamp) : null;
- }
+ @Override
+ public void onPeriodicEmit(WatermarkOutput output) {
+ // don't need to do anything because we emit in reaction to events above
+ }
}
{% endhighlight %}
@@ -316,59 +386,69 @@ public class PunctuatedAssigner implements AssignerWithPunctuatedWatermarks
-*Note:* It is possible to generate a watermark on every single event. However, because each watermark causes some
-computation downstream, an excessive number of watermarks degrades performance.
-
-
-## Timestamps per Kafka Partition
+
+Note: It is possible to
+generate a watermark on every single event. However, because each watermark
+causes some computation downstream, an excessive number of watermarks degrades
+performance.
+
-When using [Apache Kafka](connectors/kafka.html) as a data source, each Kafka partition may have a simple event time pattern (ascending
-timestamps or bounded out-of-orderness). However, when consuming streams from Kafka, multiple partitions often get consumed in parallel,
-interleaving the events from the partitions and destroying the per-partition patterns (this is inherent in how Kafka's consumer clients work).
+## Watermark Strategies and the Kafka Connector
-In that case, you can use Flink's Kafka-partition-aware watermark generation. Using that feature, watermarks are generated inside the
-Kafka consumer, per Kafka partition, and the per-partition watermarks are merged in the same way as watermarks are merged on stream shuffles.
+When using [Apache Kafka](connectors/kafka.html) as a data source, each Kafka
+partition may have a simple event time pattern (ascending timestamps or bounded
+out-of-orderness). However, when consuming streams from Kafka, multiple
+partitions often get consumed in parallel, interleaving the events from the
+partitions and destroying the per-partition patterns (this is inherent in how
+Kafka's consumer clients work).
-For example, if event timestamps are strictly ascending per Kafka partition, generating per-partition watermarks with the
-[ascending timestamps watermark generator](event_timestamp_extractors.html#assigners-with-ascending-timestamps) will result in perfect overall watermarks.
+In that case, you can use Flink's Kafka-partition-aware watermark generation.
+Using that feature, watermarks are generated inside the Kafka consumer, per
+Kafka partition, and the per-partition watermarks are merged in the same way as
+watermarks are merged on stream shuffles.
-The illustrations below show how to use the per-Kafka-partition watermark generation, and how watermarks propagate through the
-streaming dataflow in that case.
+For example, if event timestamps are strictly ascending per Kafka partition,
+generating per-partition watermarks with the [ascending timestamps watermark
+generator](event_timestamp_extractors.html#assigners-with-ascending-timestamps)
+will result in perfect overall watermarks. Note, that we don't provide a
+`TimestampAssigner` in the example, the timestamps of the Kafka records
+themselves will be used instead.
+The illustrations below show how to use the per-Kafka-partition watermark
+generation, and how watermarks propagate through the streaming dataflow in that
+case.
{% highlight scala %}
-val kafkaSource = new FlinkKafkaConsumer010[MyType]("myTopic", schema, props)
-kafkaSource.assignTimestampsAndWatermarks(new AscendingTimestampExtractor[MyType] {
- def extractAscendingTimestamp(element: MyType): Long = element.eventTimestamp
-})
+val kafkaSource = new FlinkKafkaConsumer[MyType]("myTopic", schema, props)
+kafkaSource.assignTimestampsAndWatermarks(
+ WatermarkStrategy
+ .forBoundedOutOfOrderness(Duration.ofSeconds(20)))
val stream: DataStream[MyType] = env.addSource(kafkaSource)
{% endhighlight %}
@@ -377,4 +457,31 @@ val stream: DataStream[MyType] = env.addSource(kafkaSource)
+## How Operators Process Watermarks
+
+As a general rule, operators are required to completely process a given
+watermark before forwarding it downstream. For example, `WindowOperator` will
+first evaluate all windows that should be fired, and only after producing all of
+the output triggered by the watermark will the watermark itself be sent
+downstream. In other words, all elements produced due to occurrence of a
+watermark will be emitted before the watermark.
+
+The same rule applies to `TwoInputStreamOperator`. However, in this case the
+current watermark of the operator is defined as the minimum of both of its
+inputs.
+
+The details of this behavior are defined by the implementations of the
+`OneInputStreamOperator#processWatermark`,
+`TwoInputStreamOperator#processWatermark1` and
+`TwoInputStreamOperator#processWatermark2` methods.
+
+## The Deprecated AssignerWithPeriodicWatermarks and AssignerWithPunctuatedWatermarks
+
+Prior to introducing the current abstraction of `WatermarkStrategy`,
+`TimestampAssigner`, and `WatermarkGenerator`, Flink used
+`AssignerWithPeriodicWatermarks` and `AssignerWithPeriodicWatermarks`. You will
+still see them in the API but it is recommended to use the new interfaces
+because they offer a clearer separation of concerns and also unify periodic and
+punctuated styles of watermark generation.
+
{% top %}
diff --git a/docs/dev/index.md b/docs/dev/index.md
index 0a5616b8092cbb..b58120471b371c 100644
--- a/docs/dev/index.md
+++ b/docs/dev/index.md
@@ -5,7 +5,6 @@ nav-title: ' Applica
nav-parent_id: root
nav-pos: 5
section-break: true
-always-expand: true
---
+
+Every Flink application depends on a set of Flink libraries. At the bare minimum, the application depends
+on the Flink APIs. Many applications depend in addition on certain connector libraries (like Kafka, Cassandra, etc.).
+When running Flink applications (either in a distributed deployment, or in the IDE for testing), the Flink
+runtime library must be available as well.
+
+* This will be replaced by the TOC
+{:toc}
+
+## Flink Core and Application Dependencies
+
+As with most systems that run user-defined applications, there are two broad categories of dependencies and libraries in Flink:
+
+ - **Flink Core Dependencies**: Flink itself consists of a set of classes and dependencies that are needed to run the system, for example
+ coordination, networking, checkpoints, failover, APIs, operations (such as windowing), resource management, etc.
+ The set of all these classes and dependencies forms the core of Flink's runtime and must be present when a Flink
+ application is started.
+
+ These core classes and dependencies are packaged in the `flink-dist` jar. They are part of Flink's `lib` folder and
+ part of the basic Flink container images. Think of these dependencies as similar to Java's core library (`rt.jar`, `charsets.jar`, etc.),
+ which contains the classes like `String` and `List`.
+
+ The Flink Core Dependencies do not contain any connectors or libraries (CEP, SQL, ML, etc.) in order to avoid having an excessive
+ number of dependencies and classes in the classpath by default. In fact, we try to keep the core dependencies as slim as possible
+ to keep the default classpath small and avoid dependency clashes.
+
+ - The **User Application Dependencies** are all connectors, formats, or libraries that a specific user application needs.
+
+ The user application is typically packaged into an *application jar*, which contains the application code and the required
+ connector and library dependencies.
+
+ The user application dependencies explicitly do not include the Flink DataStream APIs and runtime dependencies,
+ because those are already part of Flink's Core Dependencies.
+
+
+## Setting up a Project: Basic Dependencies
+
+Every Flink application needs as the bare minimum the API dependencies, to develop against.
+
+When setting up a project manually, you need to add the following dependencies for the Java/Scala API
+(here presented in Maven syntax, but the same dependencies apply to other build tools (Gradle, SBT, etc.) as well.
+
+
+
+**Important:** Please note that all these dependencies have their scope set to *provided*.
+That means that they are needed to compile against, but that they should not be packaged into the
+project's resulting application jar file - these dependencies are Flink Core Dependencies,
+which are already available in any setup.
+
+It is highly recommended keeping the dependencies in scope *provided*. If they are not set to *provided*,
+the best case is that the resulting JAR becomes excessively large, because it also contains all Flink core
+dependencies. The worst case is that the Flink core dependencies that are added to the application's jar file
+clash with some of your own dependency versions (which is normally avoided through inverted classloading).
+
+**Note on IntelliJ:** To make the applications run within IntelliJ IDEA it is necessary to tick the
+`Include dependencies with "Provided" scope` box in the run configuration.
+If this option is not available (possibly due to using an older IntelliJ IDEA version), then a simple workaround
+is to create a test that calls the applications `main()` method.
+
+
+## Adding Connector and Library Dependencies
+
+Most applications need specific connectors or libraries to run, for example a connector to Kafka, Cassandra, etc.
+These connectors are not part of Flink's core dependencies and must be added as dependencies to the application.
+
+Below is an example adding the connector for Kafka as a dependency (Maven syntax):
+{% highlight xml %}
+
+ org.apache.flink
+ flink-connector-kafka{{ site.scala_version_suffix }}
+ {{site.version }}
+
+{% endhighlight %}
+
+We recommend packaging the application code and all its required dependencies into one *jar-with-dependencies* which
+we refer to as the *application jar*. The application jar can be submitted to an already running Flink cluster,
+or added to a Flink application container image.
+
+Projects created from the [Java Project Template]({{ site.baseurl }}/dev/project-configuration.html) or
+[Scala Project Template]({{ site.baseurl }}/dev/project-configuration) are configured to automatically include
+the application dependencies into the application jar when running `mvn clean package`. For projects that are
+not set up from those templates, we recommend adding the Maven Shade Plugin (as listed in the Appendix below)
+to build the application jar with all required dependencies.
+
+**Important:** For Maven (and other build tools) to correctly package the dependencies into the application jar,
+these application dependencies must be specified in scope *compile* (unlike the core dependencies, which
+must be specified in scope *provided*).
+
+
+## Scala Versions
+
+Scala versions (2.11, 2.12, etc.) are not binary compatible with one another.
+For that reason, Flink for Scala 2.11 cannot be used with an application that uses
+Scala 2.12.
+
+All Flink dependencies that (transitively) depend on Scala are suffixed with the
+Scala version that they are built for, for example `flink-streaming-scala_2.11`.
+
+Developers that only use Java can pick any Scala version, Scala developers need to
+pick the Scala version that matches their application's Scala version.
+
+Please refer to the [build guide]({{ site.baseurl }}/flinkDev/building.html#scala-versions)
+for details on how to build Flink for a specific Scala version.
+
+## Hadoop Dependencies
+
+**General rule: It should never be necessary to add Hadoop dependencies directly to your application.**
+*(The only exception being when using existing Hadoop input-/output formats with Flink's Hadoop compatibility wrappers)*
+
+If you want to use Flink with Hadoop, you need to have a Flink setup that includes the Hadoop dependencies, rather than
+adding Hadoop as an application dependency. Please refer to the [Hadoop Setup Guide]({{ site.baseurl }}/ops/deployment/hadoop.html)
+for details.
+
+There are two main reasons for that design:
+
+ - Some Hadoop interaction happens in Flink's core, possibly before the user application is started, for example
+ setting up HDFS for checkpoints, authenticating via Hadoop's Kerberos tokens, or deployment on YARN.
+
+ - Flink's inverted classloading approach hides many transitive dependencies from the core dependencies. That applies not only
+ to Flink's own core dependencies, but also to Hadoop's dependencies when present in the setup.
+ That way, applications can use different versions of the same dependencies without running into dependency conflicts (and
+ trust us, that's a big deal, because Hadoops dependency tree is huge.)
+
+If you need Hadoop dependencies during testing or development inside the IDE (for example for HDFS access), please configure
+these dependencies similar to the scope of the dependencies to *test* or to *provided*.
+
+## Maven Quickstart
+
+#### Requirements
+
+The only requirements are working __Maven 3.0.4__ (or higher) and __Java 8.x__ installations.
+
+#### Create Project
+
+Use one of the following commands to __create a project__:
+
+
+{% highlight bash %}
+$ mvn archetype:generate \
+ -DarchetypeGroupId=org.apache.flink \
+ -DarchetypeArtifactId=flink-quickstart-java \{% unless site.is_stable %}
+ -DarchetypeCatalog=https://repository.apache.org/content/repositories/snapshots/ \{% endunless %}
+ -DarchetypeVersion={{site.version}}
+{% endhighlight %}
+ This allows you to name your newly created project. It will interactively ask you for the groupId, artifactId, and package name.
+
+ Note: For Maven 3.0 or higher, it is no longer possible to specify the repository (-DarchetypeCatalog) via the command line. For details about this change, please refer to Maven official document
+ If you wish to use the snapshot repository, you need to add a repository entry to your settings.xml. For example:
+{% highlight bash %}
+
+
+ apache
+
+
+
+ apache
+
+
+ apache-snapshots
+ https://repository.apache.org/content/repositories/snapshots/
+
+
+
+
+
+{% endhighlight %}
+
+ {% endunless %}
+
+
+We recommend you __import this project into your IDE__ to develop and
+test it. IntelliJ IDEA supports Maven projects out of the box.
+If you use Eclipse, the [m2e plugin](http://www.eclipse.org/m2e/)
+allows to [import Maven projects](http://books.sonatype.com/m2eclipse-book/reference/creating-sect-importing-projects.html#fig-creating-import).
+Some Eclipse bundles include that plugin by default, others require you
+to install it manually.
+
+*Please note*: The default JVM heapsize for Java may be too
+small for Flink. You have to manually increase it.
+In Eclipse, choose `Run Configurations -> Arguments` and write into the `VM Arguments` box: `-Xmx800m`.
+In IntelliJ IDEA recommended way to change JVM options is from the `Help | Edit Custom VM Options` menu. See [this article](https://intellij-support.jetbrains.com/hc/en-us/articles/206544869-Configuring-JVM-options-and-platform-properties) for details.
+
+#### Build Project
+
+If you want to __build/package your project__, go to your project directory and
+run the '`mvn clean package`' command.
+You will __find a JAR file__ that contains your application, plus connectors and libraries
+that you may have added as dependencies to the application: `target/-.jar`.
+
+__Note:__ If you use a different class than *StreamingJob* as the application's main class / entry point,
+we recommend you change the `mainClass` setting in the `pom.xml` file accordingly. That way, Flink
+can run the application from the JAR file without additionally specifying the main class.
+
+## Gradle
+
+#### Requirements
+
+The only requirements are working __Gradle 3.x__ (or higher) and __Java 8.x__ installations.
+
+#### Create Project
+
+Use one of the following commands to __create a project__:
+
+
+{% highlight gradle %}
+buildscript {
+ repositories {
+ jcenter() // this applies only to the Gradle 'Shadow' plugin
+ }
+ dependencies {
+ classpath 'com.github.jengelman.gradle.plugins:shadow:2.0.4'
+ }
+}
+
+plugins {
+ id 'java'
+ id 'application'
+ // shadow plugin to produce fat JARs
+ id 'com.github.johnrengelman.shadow' version '2.0.4'
+}
+
+
+// artifact properties
+group = 'org.myorg.quickstart'
+version = '0.1-SNAPSHOT'
+mainClassName = 'org.myorg.quickstart.StreamingJob'
+description = """Flink Quickstart Job"""
+
+ext {
+ javaVersion = '1.8'
+ flinkVersion = '{{ site.version }}'
+ scalaBinaryVersion = '{{ site.scala_version }}'
+ slf4jVersion = '1.7.15'
+ log4jVersion = '2.12.1'
+}
+
+
+sourceCompatibility = javaVersion
+targetCompatibility = javaVersion
+tasks.withType(JavaCompile) {
+ options.encoding = 'UTF-8'
+}
+
+applicationDefaultJvmArgs = ["-Dlog4j.configurationFile=log4j2.properties"]
+
+task wrapper(type: Wrapper) {
+ gradleVersion = '3.1'
+}
+
+// declare where to find the dependencies of your project
+repositories {
+ mavenCentral()
+ maven { url "https://repository.apache.org/content/repositories/snapshots/" }
+}
+
+// NOTE: We cannot use "compileOnly" or "shadow" configurations since then we could not run code
+// in the IDE or with "gradle run". We also cannot exclude transitive dependencies from the
+// shadowJar yet (see https://github.com/johnrengelman/shadow/issues/159).
+// -> Explicitly define the // libraries we want to be included in the "flinkShadowJar" configuration!
+configurations {
+ flinkShadowJar // dependencies which go into the shadowJar
+
+ // always exclude these (also from transitive dependencies) since they are provided by Flink
+ flinkShadowJar.exclude group: 'org.apache.flink', module: 'force-shading'
+ flinkShadowJar.exclude group: 'com.google.code.findbugs', module: 'jsr305'
+ flinkShadowJar.exclude group: 'org.slf4j'
+ flinkShadowJar.exclude group: 'org.apache.logging.log4j'
+}
+
+// declare the dependencies for your production and test code
+dependencies {
+ // --------------------------------------------------------------
+ // Compile-time dependencies that should NOT be part of the
+ // shadow jar and are provided in the lib folder of Flink
+ // --------------------------------------------------------------
+ compile "org.apache.flink:flink-streaming-java_${scalaBinaryVersion}:${flinkVersion}"
+
+ // --------------------------------------------------------------
+ // Dependencies that should be part of the shadow jar, e.g.
+ // connectors. These must be in the flinkShadowJar configuration!
+ // --------------------------------------------------------------
+ //flinkShadowJar "org.apache.flink:flink-connector-kafka-0.11_${scalaBinaryVersion}:${flinkVersion}"
+
+ compile "org.apache.logging.log4j:log4j-api:${log4jVersion}"
+ compile "org.apache.logging.log4j:log4j-core:${log4jVersion}"
+ compile "org.apache.logging.log4j:log4j-slf4j-impl:${log4jVersion}"
+ compile "org.slf4j:slf4j-log4j12:${slf4jVersion}"
+
+ // Add test dependencies here.
+ // testCompile "junit:junit:4.12"
+}
+
+// make compileOnly dependencies available for tests:
+sourceSets {
+ main.compileClasspath += configurations.flinkShadowJar
+ main.runtimeClasspath += configurations.flinkShadowJar
+
+ test.compileClasspath += configurations.flinkShadowJar
+ test.runtimeClasspath += configurations.flinkShadowJar
+
+ javadoc.classpath += configurations.flinkShadowJar
+}
+
+run.classpath = sourceSets.main.runtimeClasspath
+
+jar {
+ manifest {
+ attributes 'Built-By': System.getProperty('user.name'),
+ 'Build-Jdk': System.getProperty('java.version')
+ }
+}
+
+shadowJar {
+ configurations = [project.configurations.flinkShadowJar]
+}
+{% endhighlight %}
+
+{% highlight bash %}
+bash -c "$(curl https://flink.apache.org/q/gradle-quickstart.sh)" -- {{site.version}} {{site.scala_version}}
+{% endhighlight %}
+ This allows you to name your newly created project. It will interactively ask
+ you for the project name, organization (also used for the package name), project version,
+ Scala and Flink version.
+
+
+
+We recommend you __import this project into your IDE__ to develop and
+test it. IntelliJ IDEA supports Gradle projects after installing the `Gradle` plugin.
+Eclipse does so via the [Eclipse Buildship](https://projects.eclipse.org/projects/tools.buildship) plugin
+(make sure to specify a Gradle version >= 3.0 in the last step of the import wizard; the `shadow` plugin requires it).
+You may also use [Gradle's IDE integration](https://docs.gradle.org/current/userguide/userguide.html#ide-integration)
+to create project files from Gradle.
+
+
+*Please note*: The default JVM heapsize for Java may be too
+small for Flink. You have to manually increase it.
+In Eclipse, choose `Run Configurations -> Arguments` and write into the `VM Arguments` box: `-Xmx800m`.
+In IntelliJ IDEA recommended way to change JVM options is from the `Help | Edit Custom VM Options` menu. See [this article](https://intellij-support.jetbrains.com/hc/en-us/articles/206544869-Configuring-JVM-options-and-platform-properties) for details.
+
+#### Build Project
+
+If you want to __build/package your project__, go to your project directory and
+run the '`gradle clean shadowJar`' command.
+You will __find a JAR file__ that contains your application, plus connectors and libraries
+that you may have added as dependencies to the application: `build/libs/--all.jar`.
+
+__Note:__ If you use a different class than *StreamingJob* as the application's main class / entry point,
+we recommend you change the `mainClassName` setting in the `build.gradle` file accordingly. That way, Flink
+can run the application from the JAR file without additionally specifying the main class.
+
+## SBT
+
+#### Create Project
+
+You can scaffold a new project via either of the following two methods:
+
+
+{% highlight bash %}
+$ sbt new tillrohrmann/flink-project.g8
+{% endhighlight %}
+ This will prompt you for a couple of parameters (project name, flink version...) and then create a Flink project from the flink-project template.
+ You need sbt >= 0.13.13 to execute this command. You can follow this installation guide to obtain it if necessary.
+
+
+{% highlight bash %}
+$ bash <(curl https://flink.apache.org/q/sbt-quickstart.sh)
+{% endhighlight %}
+ This will create a Flink project in the specified project directory.
+
+
+
+#### Build Project
+
+In order to build your project you simply have to issue the `sbt clean assembly` command.
+This will create the fat-jar __your-project-name-assembly-0.1-SNAPSHOT.jar__ in the directory __target/scala_your-major-scala-version/__.
+
+#### Run Project
+
+In order to run your project you have to issue the `sbt run` command.
+
+Per default, this will run your job in the same JVM as `sbt` is running.
+In order to run your job in a distinct JVM, add the following line to `build.sbt`
+
+{% highlight scala %}
+fork in run := true
+{% endhighlight %}
+
+
+#### IntelliJ
+
+We recommend using [IntelliJ](https://www.jetbrains.com/idea/) for your Flink job development.
+In order to get started, you have to import your newly created project into IntelliJ.
+You can do this via `File -> New -> Project from Existing Sources...` and then choosing your project's directory.
+IntelliJ will then automatically detect the `build.sbt` file and set everything up.
+
+In order to run your Flink job, it is recommended to choose the `mainRunner` module as the classpath of your __Run/Debug Configuration__.
+This will ensure, that all dependencies which are set to _provided_ will be available upon execution.
+You can configure the __Run/Debug Configurations__ via `Run -> Edit Configurations...` and then choose `mainRunner` from the _Use classpath of module_ dropbox.
+
+#### Eclipse
+
+In order to import the newly created project into [Eclipse](https://eclipse.org/), you first have to create Eclipse project files for it.
+These project files can be created via the [sbteclipse](https://github.com/typesafehub/sbteclipse) plugin.
+Add the following line to your `PROJECT_DIR/project/plugins.sbt` file:
+
+{% highlight bash %}
+addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "4.0.0")
+{% endhighlight %}
+
+In `sbt` use the following command to create the Eclipse project files
+
+{% highlight bash %}
+> eclipse
+{% endhighlight %}
+
+Now you can import the project into Eclipse via `File -> Import... -> Existing Projects into Workspace` and then select the project directory.
+
+
+## Appendix: Template for building a Jar with Dependencies
+
+To build an application JAR that contains all dependencies required for declared connectors and libraries,
+you can use the following shade plugin definition:
+
+{% highlight xml %}
+
+
+
+ org.apache.maven.plugins
+ maven-shade-plugin
+ 3.1.1
+
+
+ package
+
+ shade
+
+
+
+
+ com.google.code.findbugs:jsr305
+ org.slf4j:*
+ log4j:*
+
+
+
+
+
+ *:*
+
+ META-INF/*.SF
+ META-INF/*.DSA
+ META-INF/*.RSA
+
+
+
+
+
+ my.programs.main.clazz
+
+
+
+
+
+
+
+
+{% endhighlight %}
+
+{% top %}
diff --git a/docs/dev/project-configuration.zh.md b/docs/dev/project-configuration.zh.md
new file mode 100644
index 00000000000000..8d5c58abb2363a
--- /dev/null
+++ b/docs/dev/project-configuration.zh.md
@@ -0,0 +1,559 @@
+---
+title: "Project Configuration"
+nav-parent_id: streaming
+nav-pos: 301
+---
+
+
+Every Flink application depends on a set of Flink libraries. At the bare minimum, the application depends
+on the Flink APIs. Many applications depend in addition on certain connector libraries (like Kafka, Cassandra, etc.).
+When running Flink applications (either in a distributed deployment, or in the IDE for testing), the Flink
+runtime library must be available as well.
+
+* This will be replaced by the TOC
+{:toc}
+
+## Flink Core and Application Dependencies
+
+As with most systems that run user-defined applications, there are two broad categories of dependencies and libraries in Flink:
+
+ - **Flink Core Dependencies**: Flink itself consists of a set of classes and dependencies that are needed to run the system, for example
+ coordination, networking, checkpoints, failover, APIs, operations (such as windowing), resource management, etc.
+ The set of all these classes and dependencies forms the core of Flink's runtime and must be present when a Flink
+ application is started.
+
+ These core classes and dependencies are packaged in the `flink-dist` jar. They are part of Flink's `lib` folder and
+ part of the basic Flink container images. Think of these dependencies as similar to Java's core library (`rt.jar`, `charsets.jar`, etc.),
+ which contains the classes like `String` and `List`.
+
+ The Flink Core Dependencies do not contain any connectors or libraries (CEP, SQL, ML, etc.) in order to avoid having an excessive
+ number of dependencies and classes in the classpath by default. In fact, we try to keep the core dependencies as slim as possible
+ to keep the default classpath small and avoid dependency clashes.
+
+ - The **User Application Dependencies** are all connectors, formats, or libraries that a specific user application needs.
+
+ The user application is typically packaged into an *application jar*, which contains the application code and the required
+ connector and library dependencies.
+
+ The user application dependencies explicitly do not include the Flink DataStream APIs and runtime dependencies,
+ because those are already part of Flink's Core Dependencies.
+
+
+## Setting up a Project: Basic Dependencies
+
+Every Flink application needs as the bare minimum the API dependencies, to develop against.
+
+When setting up a project manually, you need to add the following dependencies for the Java/Scala API
+(here presented in Maven syntax, but the same dependencies apply to other build tools (Gradle, SBT, etc.) as well.
+
+
+
+**Important:** Please note that all these dependencies have their scope set to *provided*.
+That means that they are needed to compile against, but that they should not be packaged into the
+project's resulting application jar file - these dependencies are Flink Core Dependencies,
+which are already available in any setup.
+
+It is highly recommended keeping the dependencies in scope *provided*. If they are not set to *provided*,
+the best case is that the resulting JAR becomes excessively large, because it also contains all Flink core
+dependencies. The worst case is that the Flink core dependencies that are added to the application's jar file
+clash with some of your own dependency versions (which is normally avoided through inverted classloading).
+
+**Note on IntelliJ:** To make the applications run within IntelliJ IDEA it is necessary to tick the
+`Include dependencies with "Provided" scope` box in the run configuration.
+If this option is not available (possibly due to using an older IntelliJ IDEA version), then a simple workaround
+is to create a test that calls the applications `main()` method.
+
+
+## Adding Connector and Library Dependencies
+
+Most applications need specific connectors or libraries to run, for example a connector to Kafka, Cassandra, etc.
+These connectors are not part of Flink's core dependencies and must be added as dependencies to the application.
+
+Below is an example adding the connector for Kafka as a dependency (Maven syntax):
+{% highlight xml %}
+
+ org.apache.flink
+ flink-connector-kafka{{ site.scala_version_suffix }}
+ {{site.version }}
+
+{% endhighlight %}
+
+We recommend packaging the application code and all its required dependencies into one *jar-with-dependencies* which
+we refer to as the *application jar*. The application jar can be submitted to an already running Flink cluster,
+or added to a Flink application container image.
+
+Projects created from the [Java Project Template]({{ site.baseurl }}/dev/project-configuration.html) or
+[Scala Project Template]({{ site.baseurl }}/dev/project-configuration) are configured to automatically include
+the application dependencies into the application jar when running `mvn clean package`. For projects that are
+not set up from those templates, we recommend adding the Maven Shade Plugin (as listed in the Appendix below)
+to build the application jar with all required dependencies.
+
+**Important:** For Maven (and other build tools) to correctly package the dependencies into the application jar,
+these application dependencies must be specified in scope *compile* (unlike the core dependencies, which
+must be specified in scope *provided*).
+
+
+## Scala Versions
+
+Scala versions (2.11, 2.12, etc.) are not binary compatible with one another.
+For that reason, Flink for Scala 2.11 cannot be used with an application that uses
+Scala 2.12.
+
+All Flink dependencies that (transitively) depend on Scala are suffixed with the
+Scala version that they are built for, for example `flink-streaming-scala_2.11`.
+
+Developers that only use Java can pick any Scala version, Scala developers need to
+pick the Scala version that matches their application's Scala version.
+
+Please refer to the [build guide]({{ site.baseurl }}/flinkDev/building.html#scala-versions)
+for details on how to build Flink for a specific Scala version.
+
+## Hadoop Dependencies
+
+**General rule: It should never be necessary to add Hadoop dependencies directly to your application.**
+*(The only exception being when using existing Hadoop input-/output formats with Flink's Hadoop compatibility wrappers)*
+
+If you want to use Flink with Hadoop, you need to have a Flink setup that includes the Hadoop dependencies, rather than
+adding Hadoop as an application dependency. Please refer to the [Hadoop Setup Guide]({{ site.baseurl }}/ops/deployment/hadoop.html)
+for details.
+
+There are two main reasons for that design:
+
+ - Some Hadoop interaction happens in Flink's core, possibly before the user application is started, for example
+ setting up HDFS for checkpoints, authenticating via Hadoop's Kerberos tokens, or deployment on YARN.
+
+ - Flink's inverted classloading approach hides many transitive dependencies from the core dependencies. That applies not only
+ to Flink's own core dependencies, but also to Hadoop's dependencies when present in the setup.
+ That way, applications can use different versions of the same dependencies without running into dependency conflicts (and
+ trust us, that's a big deal, because Hadoops dependency tree is huge.)
+
+If you need Hadoop dependencies during testing or development inside the IDE (for example for HDFS access), please configure
+these dependencies similar to the scope of the dependencies to *test* or to *provided*.
+
+## Maven Quickstart
+
+#### Requirements
+
+The only requirements are working __Maven 3.0.4__ (or higher) and __Java 8.x__ installations.
+
+#### Create Project
+
+Use one of the following commands to __create a project__:
+
+
+{% highlight bash %}
+$ mvn archetype:generate \
+ -DarchetypeGroupId=org.apache.flink \
+ -DarchetypeArtifactId=flink-quickstart-java \{% unless site.is_stable %}
+ -DarchetypeCatalog=https://repository.apache.org/content/repositories/snapshots/ \{% endunless %}
+ -DarchetypeVersion={{site.version}}
+{% endhighlight %}
+ This allows you to name your newly created project. It will interactively ask you for the groupId, artifactId, and package name.
+
+ Note: For Maven 3.0 or higher, it is no longer possible to specify the repository (-DarchetypeCatalog) via the command line. For details about this change, please refer to Maven official document
+ If you wish to use the snapshot repository, you need to add a repository entry to your settings.xml. For example:
+{% highlight bash %}
+
+
+ apache
+
+
+
+ apache
+
+
+ apache-snapshots
+ https://repository.apache.org/content/repositories/snapshots/
+
+
+
+
+
+{% endhighlight %}
+
+ {% endunless %}
+
+
+We recommend you __import this project into your IDE__ to develop and
+test it. IntelliJ IDEA supports Maven projects out of the box.
+If you use Eclipse, the [m2e plugin](http://www.eclipse.org/m2e/)
+allows to [import Maven projects](http://books.sonatype.com/m2eclipse-book/reference/creating-sect-importing-projects.html#fig-creating-import).
+Some Eclipse bundles include that plugin by default, others require you
+to install it manually.
+
+*Please note*: The default JVM heapsize for Java may be too
+small for Flink. You have to manually increase it.
+In Eclipse, choose `Run Configurations -> Arguments` and write into the `VM Arguments` box: `-Xmx800m`.
+In IntelliJ IDEA recommended way to change JVM options is from the `Help | Edit Custom VM Options` menu. See [this article](https://intellij-support.jetbrains.com/hc/en-us/articles/206544869-Configuring-JVM-options-and-platform-properties) for details.
+
+#### Build Project
+
+If you want to __build/package your project__, go to your project directory and
+run the '`mvn clean package`' command.
+You will __find a JAR file__ that contains your application, plus connectors and libraries
+that you may have added as dependencies to the application: `target/-.jar`.
+
+__Note:__ If you use a different class than *StreamingJob* as the application's main class / entry point,
+we recommend you change the `mainClass` setting in the `pom.xml` file accordingly. That way, Flink
+can run the application from the JAR file without additionally specifying the main class.
+
+## Gradle
+
+#### Requirements
+
+The only requirements are working __Gradle 3.x__ (or higher) and __Java 8.x__ installations.
+
+#### Create Project
+
+Use one of the following commands to __create a project__:
+
+
+{% highlight gradle %}
+buildscript {
+ repositories {
+ jcenter() // this applies only to the Gradle 'Shadow' plugin
+ }
+ dependencies {
+ classpath 'com.github.jengelman.gradle.plugins:shadow:2.0.4'
+ }
+}
+
+plugins {
+ id 'java'
+ id 'application'
+ // shadow plugin to produce fat JARs
+ id 'com.github.johnrengelman.shadow' version '2.0.4'
+}
+
+
+// artifact properties
+group = 'org.myorg.quickstart'
+version = '0.1-SNAPSHOT'
+mainClassName = 'org.myorg.quickstart.StreamingJob'
+description = """Flink Quickstart Job"""
+
+ext {
+ javaVersion = '1.8'
+ flinkVersion = '{{ site.version }}'
+ scalaBinaryVersion = '{{ site.scala_version }}'
+ slf4jVersion = '1.7.15'
+ log4jVersion = '2.12.1'
+}
+
+
+sourceCompatibility = javaVersion
+targetCompatibility = javaVersion
+tasks.withType(JavaCompile) {
+ options.encoding = 'UTF-8'
+}
+
+applicationDefaultJvmArgs = ["-Dlog4j.configurationFile=log4j2.properties"]
+
+task wrapper(type: Wrapper) {
+ gradleVersion = '3.1'
+}
+
+// declare where to find the dependencies of your project
+repositories {
+ mavenCentral()
+ maven { url "https://repository.apache.org/content/repositories/snapshots/" }
+}
+
+// NOTE: We cannot use "compileOnly" or "shadow" configurations since then we could not run code
+// in the IDE or with "gradle run". We also cannot exclude transitive dependencies from the
+// shadowJar yet (see https://github.com/johnrengelman/shadow/issues/159).
+// -> Explicitly define the // libraries we want to be included in the "flinkShadowJar" configuration!
+configurations {
+ flinkShadowJar // dependencies which go into the shadowJar
+
+ // always exclude these (also from transitive dependencies) since they are provided by Flink
+ flinkShadowJar.exclude group: 'org.apache.flink', module: 'force-shading'
+ flinkShadowJar.exclude group: 'com.google.code.findbugs', module: 'jsr305'
+ flinkShadowJar.exclude group: 'org.slf4j'
+ flinkShadowJar.exclude group: 'org.apache.logging.log4j'
+}
+
+// declare the dependencies for your production and test code
+dependencies {
+ // --------------------------------------------------------------
+ // Compile-time dependencies that should NOT be part of the
+ // shadow jar and are provided in the lib folder of Flink
+ // --------------------------------------------------------------
+ compile "org.apache.flink:flink-streaming-java_${scalaBinaryVersion}:${flinkVersion}"
+
+ // --------------------------------------------------------------
+ // Dependencies that should be part of the shadow jar, e.g.
+ // connectors. These must be in the flinkShadowJar configuration!
+ // --------------------------------------------------------------
+ //flinkShadowJar "org.apache.flink:flink-connector-kafka-0.11_${scalaBinaryVersion}:${flinkVersion}"
+
+ compile "org.apache.logging.log4j:log4j-api:${log4jVersion}"
+ compile "org.apache.logging.log4j:log4j-core:${log4jVersion}"
+ compile "org.apache.logging.log4j:log4j-slf4j-impl:${log4jVersion}"
+ compile "org.slf4j:slf4j-log4j12:${slf4jVersion}"
+
+ // Add test dependencies here.
+ // testCompile "junit:junit:4.12"
+}
+
+// make compileOnly dependencies available for tests:
+sourceSets {
+ main.compileClasspath += configurations.flinkShadowJar
+ main.runtimeClasspath += configurations.flinkShadowJar
+
+ test.compileClasspath += configurations.flinkShadowJar
+ test.runtimeClasspath += configurations.flinkShadowJar
+
+ javadoc.classpath += configurations.flinkShadowJar
+}
+
+run.classpath = sourceSets.main.runtimeClasspath
+
+jar {
+ manifest {
+ attributes 'Built-By': System.getProperty('user.name'),
+ 'Build-Jdk': System.getProperty('java.version')
+ }
+}
+
+shadowJar {
+ configurations = [project.configurations.flinkShadowJar]
+}
+{% endhighlight %}
+
+{% highlight bash %}
+bash -c "$(curl https://flink.apache.org/q/gradle-quickstart.sh)" -- {{site.version}} {{site.scala_version}}
+{% endhighlight %}
+ This allows you to name your newly created project. It will interactively ask
+ you for the project name, organization (also used for the package name), project version,
+ Scala and Flink version.
+
+
+
+We recommend you __import this project into your IDE__ to develop and
+test it. IntelliJ IDEA supports Gradle projects after installing the `Gradle` plugin.
+Eclipse does so via the [Eclipse Buildship](https://projects.eclipse.org/projects/tools.buildship) plugin
+(make sure to specify a Gradle version >= 3.0 in the last step of the import wizard; the `shadow` plugin requires it).
+You may also use [Gradle's IDE integration](https://docs.gradle.org/current/userguide/userguide.html#ide-integration)
+to create project files from Gradle.
+
+
+*Please note*: The default JVM heapsize for Java may be too
+small for Flink. You have to manually increase it.
+In Eclipse, choose `Run Configurations -> Arguments` and write into the `VM Arguments` box: `-Xmx800m`.
+In IntelliJ IDEA recommended way to change JVM options is from the `Help | Edit Custom VM Options` menu. See [this article](https://intellij-support.jetbrains.com/hc/en-us/articles/206544869-Configuring-JVM-options-and-platform-properties) for details.
+
+#### Build Project
+
+If you want to __build/package your project__, go to your project directory and
+run the '`gradle clean shadowJar`' command.
+You will __find a JAR file__ that contains your application, plus connectors and libraries
+that you may have added as dependencies to the application: `build/libs/--all.jar`.
+
+__Note:__ If you use a different class than *StreamingJob* as the application's main class / entry point,
+we recommend you change the `mainClassName` setting in the `build.gradle` file accordingly. That way, Flink
+can run the application from the JAR file without additionally specifying the main class.
+
+## SBT
+
+#### Create Project
+
+You can scaffold a new project via either of the following two methods:
+
+
+{% highlight bash %}
+$ sbt new tillrohrmann/flink-project.g8
+{% endhighlight %}
+ This will prompt you for a couple of parameters (project name, flink version...) and then create a Flink project from the flink-project template.
+ You need sbt >= 0.13.13 to execute this command. You can follow this installation guide to obtain it if necessary.
+
+
+{% highlight bash %}
+$ bash <(curl https://flink.apache.org/q/sbt-quickstart.sh)
+{% endhighlight %}
+ This will create a Flink project in the specified project directory.
+
+
+
+#### Build Project
+
+In order to build your project you simply have to issue the `sbt clean assembly` command.
+This will create the fat-jar __your-project-name-assembly-0.1-SNAPSHOT.jar__ in the directory __target/scala_your-major-scala-version/__.
+
+#### Run Project
+
+In order to run your project you have to issue the `sbt run` command.
+
+Per default, this will run your job in the same JVM as `sbt` is running.
+In order to run your job in a distinct JVM, add the following line to `build.sbt`
+
+{% highlight scala %}
+fork in run := true
+{% endhighlight %}
+
+
+#### IntelliJ
+
+We recommend using [IntelliJ](https://www.jetbrains.com/idea/) for your Flink job development.
+In order to get started, you have to import your newly created project into IntelliJ.
+You can do this via `File -> New -> Project from Existing Sources...` and then choosing your project's directory.
+IntelliJ will then automatically detect the `build.sbt` file and set everything up.
+
+In order to run your Flink job, it is recommended to choose the `mainRunner` module as the classpath of your __Run/Debug Configuration__.
+This will ensure, that all dependencies which are set to _provided_ will be available upon execution.
+You can configure the __Run/Debug Configurations__ via `Run -> Edit Configurations...` and then choose `mainRunner` from the _Use classpath of module_ dropbox.
+
+#### Eclipse
+
+In order to import the newly created project into [Eclipse](https://eclipse.org/), you first have to create Eclipse project files for it.
+These project files can be created via the [sbteclipse](https://github.com/typesafehub/sbteclipse) plugin.
+Add the following line to your `PROJECT_DIR/project/plugins.sbt` file:
+
+{% highlight bash %}
+addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "4.0.0")
+{% endhighlight %}
+
+In `sbt` use the following command to create the Eclipse project files
+
+{% highlight bash %}
+> eclipse
+{% endhighlight %}
+
+Now you can import the project into Eclipse via `File -> Import... -> Existing Projects into Workspace` and then select the project directory.
+
+
+## Appendix: Template for building a Jar with Dependencies
+
+To build an application JAR that contains all dependencies required for declared connectors and libraries,
+you can use the following shade plugin definition:
+
+{% highlight xml %}
+
+
+
+ org.apache.maven.plugins
+ maven-shade-plugin
+ 3.1.1
+
+
+ package
+
+ shade
+
+
+
+
+ com.google.code.findbugs:jsr305
+ org.slf4j:*
+ log4j:*
+
+
+
+
+
+ *:*
+
+ META-INF/*.SF
+ META-INF/*.DSA
+ META-INF/*.RSA
+
+
+
+
+
+ my.programs.main.clazz
+
+
+
+
+
+
+
+
+{% endhighlight %}
+
+{% top %}
diff --git a/docs/dev/stream/operators/index.zh.md b/docs/dev/stream/operators/index.zh.md
index 2f543352342280..29d61b5a8b0261 100644
--- a/docs/dev/stream/operators/index.zh.md
+++ b/docs/dev/stream/operators/index.zh.md
@@ -100,7 +100,7 @@ dataStream.filter(new FilterFunction() {
KeyBy DataStream → KeyedStream
-
Logically partitions a stream into disjoint partitions. All records with the same key are assigned to the same partition. Internally, keyBy() is implemented with hash partitioning. There are different ways to specify keys.
+
Logically partitions a stream into disjoint partitions. All records with the same key are assigned to the same partition. Internally, keyBy() is implemented with hash partitioning. There are different ways to specify keys.
This transformation returns a KeyedStream, which is, among other things, required to use keyed state.
Logically partitions a stream into disjoint partitions, each partition containing elements of the same key.
- Internally, this is implemented with hash partitioning. See keys on how to specify keys.
+ Internally, this is implemented with hash partitioning. See keys on how to specify keys.
This transformation returns a KeyedStream.
{% highlight scala %}
dataStream.keyBy("someKey") // Key by field "someKey"
diff --git a/docs/dev/stream/operators/windows.zh.md b/docs/dev/stream/operators/windows.zh.md
index 72269a45596d99..517f7b33863080 100644
--- a/docs/dev/stream/operators/windows.zh.md
+++ b/docs/dev/stream/operators/windows.zh.md
@@ -94,7 +94,7 @@ Using the `keyBy(...)` will split your infinite stream into logical keyed stream
stream is not keyed.
In the case of keyed streams, any attribute of your incoming events can be used as a key
-(more details [here]({{ site.baseurl }}/dev/api_concepts.html#specifying-keys)). Having a keyed stream will
+(more details [here]({% link dev/stream/state/state.zh.md %}#keyed-datastream)). Having a keyed stream will
allow your windowed computation to be performed in parallel by multiple tasks, as each logical keyed stream can be processed
independently from the rest. All elements referring to the same key will be sent to the same parallel task.
diff --git a/docs/dev/stream/sources.md b/docs/dev/stream/sources.md
new file mode 100644
index 00000000000000..3c3db9085e12bf
--- /dev/null
+++ b/docs/dev/stream/sources.md
@@ -0,0 +1,391 @@
+---
+title: "Data Sources"
+nav-title: "Data Sources"
+nav-parent_id: streaming
+nav-pos: 10
+---
+
+
+
+* This will be replaced by the TOC
+{:toc}
+
+
+
Note: This describes the new Data Source API, introduced in Flink 1.11 as part of FLIP-27.
+ This new API is currently in BETA status.
+
Most of the existing source connectors are not yet (as of Flink 1.11) implemented using this new API,
+ but using the previous API, based on SourceFunction.
+
+
+
+This page describes Flink's Data Source API and the concepts and architecture behind it.
+**Read this, if you are interested in how data sources in Flink work, or if you want to implement a new Data Source.**
+
+If you are looking for pre-defined source connectors, please check the [Connector Docs]({{ site.baseurl }}/dev/connectors/).
+
+
+## Data Source Concepts
+
+**Core Components**
+
+A Data Source has three core components: *Splits*, the *SplitEnumerator*, and the *SourceReader*.
+
+ - A **Split** is a portion of data consumed by the source, like a file or a log partition. Splits are granularity by which the source distributes the work and parallelizes the data reading.
+
+ - The **SourceReader** requests *Splits* and processes them, for example by reading the file or log partition represented by the *Split*. The *SourceReader* run in parallel on the Task Managers in the `SourceOperators` and produces the parallel stream of events/records.
+
+ - The **SplitEnumerator** generates the *Splits* and assignes them to the *SourceReaders*. It runs as a single instance on the Job Manager and is responsible for maintaining the backlog of pending *Splits* and assigning them to the readers in a balanced manner.
+
+The [Source]() class is API entry point that ties the above three components together.
+
+
+
+
+
+
+**Unified Across Streaming and Batch**
+
+The Data Source API supports both unbounded streaming sources and bounded batch sources, in a unified way.
+
+The difference between both cases is minimal: In the bounded/batch case, the enumerator generates a fix set of splits, and each split is necessarily finite. In the unbounded streaming case, one of the two is not true (splits are not finite, or the enumerator keep generating new splits).
+
+#### Examples
+
+Here are some simplified conceptual examples to illustrate how the data source components interact, in streaming and batch cases.
+
+*Note that this does not the accurately describe how the Kafka and File source implementations work; parts are simplified, for illustrative purposes.*
+
+**Bounded File Source**
+
+The source has the URI/Path of a directory to read, and a *Format* that defines how to parse the files.
+
+ - A *Split* is a file, or a region of a file (if the data format supports splitting the file).
+ - The *SplitEnumerator* lists all files under the given directory path. It assigns Splits to the next reader that requests a Split. Once all Splits are assigned, it responds to requests with *NoMoreSplits*.
+ - The *SourceReader* requests a Split and reads the assigned Split (file or file region) and parses it using the given Format. If it does not get another Split, but a *NoMoreSplits* message, it finishes.
+
+**Unbounded Streaming File Source**
+
+This source works the same way as described above, except that the *SplitEnumerator* never responds with *NoMoreSplits* and periodically lists the contents under the given URI/Path to check for new files. Once it finds new files, it generates new Splits for them and can assign them to the available SourceReaders.
+
+**Unbounded Streaming Kafka Source**
+
+The source has a Kafka Topic (or list of Topics or Topic regex) and a *Deserializer* to parse the records.
+
+ - A *Split* is a Kafka Topic Partition.
+ - The *SplitEnumerator* connects to the brokers to list all topic partitions involved in the subscribed topics. The enumerator can optionally repeat this operation to discover newly added topics/partitions.
+ - The *SourceReader* reads the assigned Splits (Topic Partitions) using the KafkaConsumer and deserializes the records using the provided Deserializer. The splits (Topic Partitions) do not have an end, so the reader never reaches the end of the data.
+
+**Bounded Kafka Source**
+
+Same as above, except that each Split (Topic Partition) has a defined end offset. Once the *SourceReader* reaches the end offset for a Split, it finishes that Split. Once all assigned Splits are finished, the SourceReader finishes.
+
+## The Data Source API
+This section describes the major interfaces of the new Source API introduced in FLIP-27, and provides tips to the developers on the Source development.
+
+### Source
+The [Source](https://github.com/apache/flink/blob/master/flink-core/src/main/java/org/apache/flink/api/connector/source/Source.java) API is a factory style interface to create the following components.
+
+ - *Split Enumerator*
+ - *Source Reader*
+ - *Split Serializer*
+ - *Enumerator Checkpoint Serializer*
+
+In addition to that, the Source provides the [boundedness](https://github.com/apache/flink/blob/master/flink-core/src/main/java/org/apache/flink/api/connector/source/Boundedness.java) attribute of the source, so that Flink can choose appropriate mode to run the Flink jobs.
+
+The Source implementations should be serializable as the Source instances are serialized and uploaded to the Flink cluster at runtime.
+
+### SplitEnumerator
+The [SplitEnumerator](https://github.com/apache/flink/blob/master/flink-core/src/main/java/org/apache/flink/api/connector/source/SplitEnumerator.java) is expected to be the "brain" of the Source. Typical implementations of the `SplitEnumerator` do the following:
+
+ - `SourceReader` registration handling
+ - `SourceReader` failure handling
+ - The `addSplitsBack()` method will be invoked when a `SourceReader` fails. The SplitEnumerator should take back the split assignments that have not been acknowledged by the failed `SourceReader`.
+ - `SourceEvent` handling
+ - `SourceEvent`s are custom events sent between `SplitEnumerator` and `SourceReader`. The implementation can leverage this mechanism to perform sophisticated coordination.
+ - Split discovery and assignment
+ - The `SplitEnumerator` can assign splits to the `SourceReader`s in response to various events, including discovery of new splits, new `SourceReader` registration, `SourceReader` failure, etc.
+
+A `SplitEnumerator` can accomplish the above work with the help of the [SplitEnumeratorContext](https://github.com/apache/flink/blob/master/flink-core/src/main/java/org/apache/flink/api/connector/source/SplitEnumeratorContext.java) which is provided to the `Source` on creation or restore of the `SplitEnumerator`.
+The `SplitEnumeratorContext` allows a `SplitEnumerator` to retrieve necessary information of the readers and perform coordination actions.
+The `Source` implementation is expected to pass the `SplitEnumeratorContext` to the `SplitEnumerator` instance.
+
+While a `SplitEnumerator` implementation can work well in a reactive way by only taking coordination actions when its method is invoked, some `SplitEnumerator` implementations might want to take actions actively. For example, a `SplitEnumerator` may want to periodically run split discovery and assign the new splits to the `SourceReaders`.
+Such implementations may find that the `callAsync()` method `SplitEnumeratorContext` is handy. The code snippet below shows how the `SplitEnumerator` implementation can achieve that without maintaining its own threads.
+
+{% highlight java %}
+class MySplitEnumerator implements SplitEnumerator {
+ private final long DISCOVER_INTERVAL = 60_000L;
+
+ /**
+ * A method to discover the splits.
+ */
+ private List discoverSplits() {...}
+
+ @Override
+ public void start() {
+ ...
+ enumContext.callAsync(this::discoverSplits, splits -> {
+ Map> assignments = new HashMap<>();
+ int parallelism = enumContext.currentParallelism();
+ for (MockSourceSplit split : splits) {
+ int owner = split.splitId().hashCode() % parallelism;
+ assignments.computeIfAbsent(owner, new ArrayList<>()).add(split);
+ }
+ enumContext.assignSplits(new SplitsAssignment<>(assignments));
+ }, 0L, DISCOVER_INTERVAL);
+ ...
+ }
+ ...
+}
+{% endhighlight %}
+
+
+### SourceReader
+
+The [SourceReader](https://github.com/apache/flink/blob/master/flink-core/src/main/java/org/apache/flink/api/connector/source/SourceReader.java) is a component running in the Task Managers to consume the records from the Splits.
+
+The `SourceReader` exposes a pull-based consumption interface. A Flink task keeps calling `pollNext(ReaderOutput)` in a loop to poll records from the `SourceReader`. The return value of the `pollNext(ReaderOutput)` method indicates the status of the source reader.
+
+ - `MORE_AVAILABLE` - The SourceReader has more records available immediately.
+ - `NOTHING_AVAILABLE` - The SourceReader does not have more records available at this point, but may have more records in the future.
+ - `END_OF_INPUT` - The SourceReader has exhausted all the records and reached the end of data. This means the SourceReader can be closed.
+
+In the interest of performance, a `ReaderOutput` is provided to the `pollNext(ReaderOutput)` method, so a `SourceReader` can emit multiple records in a single call of pollNext() if it has to. For example, sometimes the external system works at the granularity of blocks. A block may contain multiple records but the source can only checkpoint at the block boundaries. In this case the `SourceReader` can emit all the records in one block at a time to the `ReaderOutput`.
+**However, the `SourceReader` implementation should avoid emitting multiple records in a single `pollNext(ReaderOutput)` invocation unless necessary.** This is because the task thread that is polling from the `SourceReader` works in an event-loop and cannot block.
+
+All the state of a `SourceReader` should be maintained inside the `SourceSplit`s which are returned at the `snapshotState()` invocation. Doing this allows the `SourceSplit`s to be reassigned to other `SourceReaders` when needed.
+
+A `SourceReaderContext` is provided to the `Source` upon a `SourceReader` creation. It is expected that the `Source` will pass the context to the `SourceReader` instance. The `SourceReader` can send `SourceEvent` to its `SplitEnumerator` through the `SourceReaderContext`. A typical design pattern of the `Source` is letting the `SourceReader`s report their local information to the `SplitEnumerator` who has a global view to make decisions.
+
+The `SourceReader` API is a low level API that allows users to deal with the splits manually and have their own threading model to fetch and handover the records. To facilitate the `SourceReader` implementation, Flink has provided a [SourceReaderBase](https://github.com/apache/flink/blob/master/flink-connectors/flink-connector-base/src/main/java/org/apache/flink/connector/base/source/reader/SourceReaderBase.java) class which significantly reduces the amount the work needed to write a `SourceReader`.
+**It is highly recommended for the connector developers to take advantage of the `SourceReaderBase` instead of writing the `SourceReader`s from scratch**. For more details please check the [Split Reader API](#the-split-reader-api) section.
+
+### Use the Source
+In order to create a `DataStream` from a `Source`, one needs to pass the `Source` to a `StreamExecutionEnvironment`. For example,
+
+
+
+----
+----
+
+## The Split Reader API
+
+The core SourceReader API is fully asynchronous and requires implementations to manage asynchronous split reading manually.
+However, in practice, most sources use perform blocking operations, like blocking *poll()* calls on clients (for example the `KafkaConsumer`), or blocking I/O operations on distributed file systems (HDFS, S3, ...). To make this compatible with the asynchronous Source API, these blocking (synchronous) operations need to happen in separate threads, which hand over the data to the asynchronous part of the reader.
+
+The [SplitReader](https://github.com/apache/flink/blob/master/flink-connectors/flink-connector-base/src/main/java/org/apache/flink/connector/base/source/reader/splitreader/SplitReader.java) is the high-level API for simple synchronous reading/polling-based source implementations, like file reading, Kafka, etc.
+
+The core is the `SourceReaderBase` class, which takes a `SplitReader` and creates fetcher threads running the SplitReader, supporting different consumption threading models.
+
+### SplitReader
+
+The `SplitReader` API only has three methods:
+ - A blocking fetch method to return a [RecordsWithSplitIds](https://github.com/apache/flink/blob/master/flink-connectors/flink-connector-base/src/main/java/org/apache/flink/connector/base/source/reader/RecordsWithSplitIds.java)
+ - A non-blocking method to handle split changes.
+ - A non-blocking wake up method to wake up the blocking fetch operation.
+
+The `SplitReader` only focuses on reading the records from the external system, therefore is much simpler compared with `SourceReader`.
+Please check the Java doc of the class for more details.
+
+### SourceReaderBase
+
+It is quite common that a `SourceReader` implementation does the following:
+
+ - Have a pool of threads fetching from splits of the external system in a blocking way.
+ - Handle the synchronization between the internal fetching threads and other methods invocations such as `pollNext(ReaderOutput)`.
+ - Maintain the per split watermark for watermark alignment.
+ - Maintain the state of each split for checkpoint.
+
+In order to reduce the work of writing a new `SourceReader`, Flink provides a [SourceReaderBase](https://github.com/apache/flink/blob/master/flink-connectors/flink-connector-base/src/main/java/org/apache/flink/connector/base/source/reader/SourceReaderBase.java) class to serve as a base implementation of the `SourceReader`.
+`SourceReaderBase` has all the above work done out of the box. To write a new `SourceReader`, one can just let the `SourceReader` implementation inherit from the `SourceReaderBase`, fill in a few methods and implement a high level [SplitReader](https://github.com/apache/flink/blob/master/flink-connectors/flink-connector-base/src/main/java/org/apache/flink/connector/base/source/reader/splitreader/SplitReader.java).
+
+### SplitFetcherManager
+
+The `SourceReaderBase` supports a few threading models out of the box, depending on the behavior of the [SplitFetcherManager](https://github.com/apache/flink/blob/master/flink-connectors/flink-connector-base/src/main/java/org/apache/flink/connector/base/source/reader/fetcher/SplitFetcherManager.java) it works with.
+The `SplitFetcherManager` helps create and maintain a pool of `SplitFetcher`s each fetching with a `SplitReader`. It also determines how to assign splits to each split fetcher.
+
+As an example, as illustrated below, a `SplitFetcherManager` may have a fixed number of threads, each fetching from some splits assigned to the `SourceReader`.
+
+
+
+The following code snippet implements the this threading model.
+
+{% highlight java %}
+/**
+ * A SplitFetcherManager that has a fixed size of split fetchers and assign splits
+ * to the split fetchers based on the hash code of split IDs.
+ */
+public class FixedSizeSplitFetcherManager
+ extends SplitFetcherManager {
+ private final int numFetchers;
+
+ public FixedSizeSplitFetcherManager(
+ int numFetchers,
+ FutureNotifier futureNotifier,
+ FutureCompletingBlockingQueue> elementsQueue,
+ Supplier> splitReaderSupplier) {
+ super(futureNotifier, elementsQueue, splitReaderSupplier);
+ this.numFetchers = numFetchers;
+ // Create numFetchers split fetchers.
+ for (int i = 0; i < numFetchers; i++) {
+ startFetcher(createSplitFetcher());
+ }
+ }
+
+ @Override
+ public void addSplits(List splitsToAdd) {
+ // Group splits by their owner fetchers.
+ Map> splitsByFetcherIndex = new HashMap<>();
+ splitsToAdd.forEach(split -> {
+ int ownerFetcherIndex = split.hashCode() % numFetchers;
+ splitsByFetcherIndex
+ .computeIfAbsent(ownerFetcherIndex, s -> new ArrayList<>())
+ .add(split);
+ });
+ // Assign the splits to their owner fetcher.
+ splitsByFetcherIndex.forEach((fetcherIndex, splitsForFetcher) -> {
+ fetchers.get(fetcherIndex).addSplits(splitsForFetcher);
+ });
+ }
+}
+{% endhighlight %}
+
+
+And a `SourceReader` using this threading model can be created like following:
+
+
+Apparently, the `SourceReader` implementations can also implement their own threading model easily on top of the `SplitFetcherManager` and `SourceReaderBase`.
+
+## Event Time and Watermarks
+
+*Event Time* assignment and *Watermark Generation* happen as part of the data sources. The event streams leaving the Source Readers have event timestamps and (during streaming execution) contain watermarks. See [Timely Stream Processing]({{ site.baseurl }}/concepts/timely-stream-processing.html) for an introduction to Event Time and Watermarks.
+
+Important Applications based on the legacy [SourceFunction](https://github.com/apache/flink/blob/master/flink-streaming-java/src/main/java/org/apache/flink/streaming/api/functions/source/SourceFunction.java) typically generate timestamps and watermarks in a separate later step via `stream.assignTimestampsAndWatermarks(WatermarkStrategy)`. This function should not be used with the new sources, because timestamps will be already assigned, and it will override the previous split-aware watermarks.
+
+#### API
+
+The `WatermarkStrategy` is passed to the Source during creation in the DataStream API and creates both the [TimestampAssigner](https://github.com/apache/flink/blob/master/flink-core/src/main/java/org/apache/flink/api/common/eventtime/TimestampAssigner.java) and [WatermarkGenerator](https://github.com/apache/flink/blob/master/flink-core/src/main/java/org/apache/flink/api/common/eventtime/WatermarkGenerator.java).
+
+{% highlight java %}
+environment.fromSource(
+ Source source,
+ WatermarkStrategy timestampsAndWatermarks,
+ String sourceName)
+{% endhighlight %}
+
+The `TimestampAssigner` and `WatermarkGenerator` run transparently as part of the `ReaderOutput`(or `SourceOutput`) so source implementors do not have to implement any timestamp extraction and watermark generation code.
+
+#### Event Timestamps
+
+Event timestamps are assigned in two steps:
+
+ 1. The SourceReader may attach the *source record timestamp* to the event, by calling `SourceOutput.collect(event, timestamp)`.
+ This is relevant only for data sources that are record-based and have timestamps, such as Kafka, Kinesis, Pulsar, or Pravega.
+ Sources that are not based on records with timestamps (like files) do not have a *source record timestamp*.
+ This step is part of the source connector implementation and not parameterized by the application that uses the source.
+
+ 2. The `TimestampAssigner`, which is configured by the application, assigns the final timestamp.
+ The `TimestampAssigner` sees the original *source record timestamp* and the event. The assigner can use the *source record timestamp* or access a field of the event obtain the final event timestamp.
+
+This two-step approach allows users to reference both timestamps from the source systems and timestamps in the event's data as the event timestamp.
+
+*Note:* When using a data source without *source record timestamps* (like files) and selecting the *source record timestamp* as the final event timestamp, events will get a default timestamp equal to `LONG_MIN` *(=-9,223,372,036,854,775,808)*.
+
+#### Watermark Generation
+
+Watermark Generators are only active during streaming execution. Batch execution deactivates Watermark Generators; all related operations described below become effectively no-ops.
+
+The data source API supports running watermark generators individually *per split*. That allows Flink to observe the event time progress per split individually, which is important to handle *event time skew* properly and prevent *idle partitions* from holding back the event time progress of the entire application.
+
+
+
+
+
+When implementing a source connector using the *Split Reader API*, this is automatically handled. All implementations based on the Split Reader API have split-aware watermarks out-of-the-box.
+
+For an implementation of the lower level `SourceReader` API to use split-aware watermark generation, the implementation must ouput events from different splits to different outputs: the *Split-local SourceOutputs*. Split-local outputs can be created and released on the main [ReaderOutput](https://github.com/apache/flink/blob/master/flink-core/src/main/java/org/apache/flink/api/connector/source/ReaderOutput.java) via the `createOutputForSplit(splitId)` and `releaseOutputForSplit(splitId)` methods. Please refer to the JavaDocs of the class and methods for details.
diff --git a/docs/dev/stream/sources.zh.md b/docs/dev/stream/sources.zh.md
new file mode 100644
index 00000000000000..a063ecb54b2dae
--- /dev/null
+++ b/docs/dev/stream/sources.zh.md
@@ -0,0 +1,391 @@
+---
+title: "Data Sources"
+nav-title: "Data Sources"
+nav-parent_id: streaming
+nav-pos: 10
+---
+
+
+
+* This will be replaced by the TOC
+{:toc}
+
+
+
Note: This describes the new Data Source API, introduced in Flink 1.11 as part of FLIP-27.
+ This new API is currently in BETA status.
+
Most of the existing source connectors are not yet (as of Flink 1.11) implemented using this new API,
+ but using the previous API, based on SourceFunction.
+
+
+
+This page describes Flink's Data Source API and the concepts and architecture behind it.
+**Read this, if you are interested in how data sources in Flink work, or if you want to implement a new Data Source.**
+
+If you are looking for pre-defined source connectors, please check the [Connector Docs]({{ site.baseurl }}/dev/connectors/).
+
+
+## Data Source Concepts
+
+**Core Components**
+
+A Data Source has three core components: *Splits*, the *SplitEnumerator*, and the *SourceReader*.
+
+ - A **Split** is a portion of data consumed by the source, like a file or a log partition. Splits are granularity by which the source distributes the work and parallelizes the data reading.
+
+ - The **SourceReader** requests *Splits* and processes them, for example by reading the file or log partition represented by the *Split*. The *SourceReader* run in parallel on the Task Managers in the `SourceOperators` and produces the parallel stream of events/records.
+
+ - The **SplitEnumerator** generates the *Splits* and assignes them to the *SourceReaders*. It runs as a single instance on the Job Manager and is responsible for maintaining the backlog of pending *Splits* and assigning them to the readers in a balanced manner.
+
+The [Source]() class is API entry point that ties the above three components together.
+
+
+
+
+
+
+**Unified Across Streaming and Batch**
+
+The Data Source API supports both unbounded streaming sources and bounded batch sources, in a unified way.
+
+The difference between both cases is minimal: In the bounded/batch case, the enumerator generates a fix set of splits, and each split is necessarily finite. In the unbounded streaming case, one of the two is not true (splits are not finite, or the enumerator keep generating new splits).
+
+#### Examples
+
+Here are some simplified conceptual examples to illustrate how the data source components interact, in streaming and batch cases.
+
+*Note that this does not the accurately describe how the Kafka and File source implementations work; parts are simplified, for illustrative purposes.*
+
+**Bounded File Source**
+
+The source has the URI/Path of a directory to read, and a *Format* that defines how to parse the files.
+
+ - A *Split* is a file, or a region of a file (if the data format supports splitting the file).
+ - The *SplitEnumerator* lists all files under the given directory path. It assigns Splits to the next reader that requests a Split. Once all Splits are assigned, it responds to requests with *NoMoreSplits*.
+ - The *SourceReader* requests a Split and reads the assigned Split (file or file region) and parses it using the given Format. If it does not get another Split, but a *NoMoreSplits* message, it finishes.
+
+**Unbounded Streaming File Source**
+
+This source works the same way as described above, except that the *SplitEnumerator* never responds with *NoMoreSplits* and periodically lists the contents under the given URI/Path to check for new files. Once it finds new files, it generates new Splits for them and can assign them to the available SourceReaders.
+
+**Unbounded Streaming Kafka Source**
+
+The source has a Kafka Topic (or list of Topics or Topic regex) and a *Deserializer* to parse the records.
+
+ - A *Split* is a Kafka Topic Partition.
+ - The *SplitEnumerator* connects to the brokers to list all topic partitions involved in the subscribed topics. The enumerator can optionally repeat this operation to discover newly added topics/partitions.
+ - The *SourceReader* reads the assigned Splits (Topic Partitions) using the KafkaConsumer and deserializes the records using the provided Deserializer. The splits (Topic Partitions) do not have an end, so the reader never reaches the end of the data.
+
+**Bounded Kafka Source**
+
+Same as above, except that each Split (Topic Partition) has a defined end offset. Once the *SourceReader* reaches the end offset for a Split, it finishes that Split. Once all assigned Splits are finished, the SourceReader finishes.
+
+## The Data Source API
+This section describes the major interfaces of the new Source API introduced in FLIP-27, and provides tips to the developers on the Source development.
+
+### Source
+The [Source](https://github.com/apache/flink/blob/master/flink-core/src/main/java/org/apache/flink/api/connector/source/Source.java) API is a factory style interface to create the following components.
+
+ - *Split Enumerator*
+ - *Source Reader*
+ - *Split Serializer*
+ - *Enumerator Checkpoint Serializer*
+
+In addition to that, the Source provides the [boundedness](https://github.com/apache/flink/blob/master/flink-core/src/main/java/org/apache/flink/api/connector/source/Boundedness.java) attribute of the source, so that Flink can choose appropriate mode to run the Flink jobs.
+
+The Source implementations should be serializable as the Source instances are serialized and uploaded to the Flink cluster at runtime.
+
+### SplitEnumerator
+The [SplitEnumerator](https://github.com/apache/flink/blob/master/flink-core/src/main/java/org/apache/flink/api/connector/source/SplitEnumerator.java) is expected to be the "brain" of the Source. Typical implementations of the `SplitEnumerator` do the following:
+
+ - `SourceReader` registration handling
+ - `SourceReader` failure handling
+ - The `addSplitsBack()` method will be invoked when a `SourceReader` fails. The SplitEnumerator should take back the split assignments that have not been acknowledged by the failed `SourceReader`.
+ - `SourceEvent` handling
+ - `SourceEvent`s are custom events sent between `SplitEnumerator` and `SourceReader`. The implementation can leverage this mechanism to perform sophisticated coordination.
+ - Split discovery and assignment
+ - The `SplitEnumerator` can assign splits to the `SourceReader`s in response to various events, including discovery of new splits, new `SourceReader` registration, `SourceReader` failure, etc.
+
+A `SplitEnumerator` can accomplish the above work with the help of the [SplitEnumeratorContext](https://github.com/apache/flink/blob/master/flink-core/src/main/java/org/apache/flink/api/connector/source/SplitEnumeratorContext.java) which is provided to the `Source` on creation or restore of the `SplitEnumerator`.
+The `SplitEnumeratorContext` allows a `SplitEnumerator` to retrieve necessary information of the readers and perform coordination actions.
+The `Source` implementation is expected to pass the `SplitEnumeratorContext` to the `SplitEnumerator` instance.
+
+While a `SplitEnumerator` implementation can work well in a reactive way by only taking coordination actions when its method is invoked, some `SplitEnumerator` implementations might want to take actions actively. For example, a `SplitEnumerator` may want to periodically run split discovery and assign the new splits to the `SourceReaders`.
+Such implementations may find that the `callAsync()` method `SplitEnumeratorContext` is handy. The code snippet below shows how the `SplitEnumerator` implementation can achieve that without maintaining its own threads.
+
+{% highlight java %}
+class MySplitEnumerator implements SplitEnumerator {
+ private final long DISCOVER_INTERVAL = 60_000L;
+
+ /**
+ * A method to discover the splits.
+ */
+ private List discoverSplits() {...}
+
+ @Override
+ public void start() {
+ ...
+ enumContext.callAsync(this::discoverSplits, splits -> {
+ Map> assignments = new HashMap<>();
+ int parallelism = enumContext.currentParallelism();
+ for (MockSourceSplit split : splits) {
+ int owner = split.splitId().hashCode() % parallelism;
+ assignments.computeIfAbsent(owner, new ArrayList<>()).add(split);
+ }
+ enumContext.assignSplits(new SplitsAssignment<>(assignments));
+ }, 0L, DISCOVER_INTERVAL);
+ ...
+ }
+ ...
+}
+{% endhighlight %}
+
+
+### SourceReader
+
+The [SourceReader](https://github.com/apache/flink/blob/master/flink-core/src/main/java/org/apache/flink/api/connector/source/SourceReader.java) is a component running in the Task Managers to consume the records from the Splits.
+
+The `SourceReader` exposes a pull-based consumption interface. A Flink task keeps calling `pollNext(ReaderOutput)` in a loop to poll records from the `SourceReader`. The return value of the `pollNext(ReaderOutput)` method indicates the status of the source reader.
+
+ - `MORE_AVAILABLE` - The SourceReader has more records available intermediately.
+ - `NOTHING_AVAILABLE` - The SourceReader does not have more records available at this point, but may have more records in the future.
+ - `END_OF_INPUT` - The SourceReader has exhausted all the records and reached the end of data. This means the SourceReader can be closed.
+
+In the interest of performance, a `ReaderOutput` is provided to the `pollNext(ReaderOutput)` method, so a `SourceReader` can emit multiple records in a single call of pollNext() if it has to. For example, sometimes the external system works at the granularity of blocks. A block may contain multiple records but the source can only checkpoint at the block boundaries. In this case the `SourceReader` can emit one block at a time to the `ReaderOutput`.
+**However, the `SourceReader` implementation should avoid emitting multiple records in a single `pollNext(ReaderOutput)` invocation unless necessary.**
+
+All the state of a `SourceReader` should be maintained inside the `SourceSplit`s which are returned at the `snapshotState()` invocation. Doing this allows the `SourceSplit`s to be reassigned to other `SourceReaders` when needed.
+
+A `SourceReaderContext` is provided to the `Source` upon a `SourceReader` creation. It is expected that the `Source` will pass the context to the `SourceReader` instance. The `SourceReader` can send `SourceEvent` to its `SplitEnumerator`. A typical design pattern for the `Source` is letting the `SourceReader`s report their local information to the `SplitEnumerator` who has a global view to make decisions.
+
+The `SourceReader` API is a low level API that allows users to deal with the splits manually and have their own threading model to fetch and handover the records. To facilitate the `SourceReader` implementation, Flink has provided a [SourceReaderBase](https://github.com/apache/flink/blob/master/flink-connectors/flink-connector-base/src/main/java/org/apache/flink/connector/base/source/reader/SourceReaderBase.java) class which significantly reduce the amount the work needed to write a `SourceReader`.
+**It is highly recommended for the connector developers to take advantage of the `SourceReaderBase` instead of writing the `SourceReader`s by themselves from scratch**. For more details please check the [Split Reader API](#the-split-reader-api) section.
+
+### Use the Source
+In order to create a `DataStream` from a `Source`, one needs to pass the `Source` to a `StreamExecutionEnvironment`. For example,
+
+
+
+----
+----
+
+## The Split Reader API
+
+The core SourceReader API is fully asynchronous and requires implementations to manage asynchronous split reading manually.
+However, in practice, most sources use perform blocking operations, like blocking *poll()* calls on clients (for example the `KafkaConsumer`), or blocking I/O operations on distributed file systems (HDFS, S3, ...). To make this compatible with the asynchronous Source API, these blocking (synchronous) operations need to happen in separate threads, which hand over the data to the asynchronous part of the reader.
+
+The [SplitReader](https://github.com/apache/flink/blob/master/flink-connectors/flink-connector-base/src/main/java/org/apache/flink/connector/base/source/reader/splitreader/SplitReader.java) is the high-level API for simple synchronous reading/polling-based source implementations, like file reading, Kafka, etc.
+
+The core is the `SourceReaderBase` class, which takes a `SplitReader` and creates fetcher threads running the SplitReader, supporting different consumption threading models.
+
+### SplitReader
+
+The `SplitReader` API only has three methods:
+ - A blocking fetch method to return a [RecordsWithSplitIds](https://github.com/apache/flink/blob/master/flink-connectors/flink-connector-base/src/main/java/org/apache/flink/connector/base/source/reader/RecordsWithSplitIds.java)
+ - A non-blocking method to handle split changes.
+ - A non-blocking wake up method to wake up the blocking fetch operation.
+
+The `SplitReader` only focuses on reading the records from the external system, therefore is much simpler compared with `SourceReader`.
+Please check the Java doc of the class for more details.
+
+### SourceReaderBase
+
+It is quite common that a `SourceReader` implementation does the following:
+
+ - Have a pool of threads fetching from splits of the external system in a blocking way.
+ - Handle the synchronization between the fetching threads mentioned above and the `pollNext(ReaderOutput)` invocation.
+ - Maintain the per split watermark for watermark alignment.
+ - Maintain the state of each split for checkpoint
+
+In order to reduce the work of writing a new `SourceReader`, Flink provides a [SourceReaderBase](https://github.com/apache/flink/blob/master/flink-connectors/flink-connector-base/src/main/java/org/apache/flink/connector/base/source/reader/SourceReaderBase.java) class to serve as a base implementation of the `SourceReader`.
+`SourceReaderBase` has all the above work done out of the box. To write a new `SourceReader`, one can just let the `SourceReader` implementation inherit from the `SourceReaderBase`, fill in a few methods and implement a high level [SplitReader](https://github.com/apache/flink/blob/master/flink-connectors/flink-connector-base/src/main/java/org/apache/flink/connector/base/source/reader/splitreader/SplitReader.java).
+
+### SplitFetcherManager
+
+The `SourceReaderBase` supports a few threading models out of the box, depending on the behavior of the [SplitFetcherManager](https://github.com/apache/flink/blob/master/flink-connectors/flink-connector-base/src/main/java/org/apache/flink/connector/base/source/reader/fetcher/SplitFetcherManager.java) it works with.
+The `SplitFetcherManager` helps create and maintain a pool of `SplitFetcher`s each fetching with a `SplitReader`. It also determines how to assign splits to each split fetcher.
+
+As an example, as illustrated below, a `SplitFetcherManager` may have a fixed number of threads, each fetching from some splits assigned to the `SourceReader`.
+
+
+
+The following code snippet implements the this threading model.
+
+{% highlight java %}
+/**
+ * A SplitFetcherManager that has a fixed size of split fetchers and assign splits
+ * to the split fetchers based on the hash code of split IDs.
+ */
+public class FixedSizeSplitFetcherManager
+ extends SplitFetcherManager {
+ private final int numFetchers;
+
+ public FixedSizeSplitFetcherManager(
+ int numFetchers,
+ FutureNotifier futureNotifier,
+ FutureCompletingBlockingQueue> elementsQueue,
+ Supplier> splitReaderSupplier) {
+ super(futureNotifier, elementsQueue, splitReaderSupplier);
+ this.numFetchers = numFetchers;
+ // Create numFetchers split fetchers.
+ for (int i = 0; i < numFetchers; i++) {
+ startFetcher(createSplitFetcher());
+ }
+ }
+
+ @Override
+ public void addSplits(List splitsToAdd) {
+ // Group splits by their owner fetchers.
+ Map> splitsByFetcherIndex = new HashMap<>();
+ splitsToAdd.forEach(split -> {
+ int ownerFetcherIndex = split.hashCode() % numFetchers;
+ splitsByFetcherIndex
+ .computeIfAbsent(ownerFetcherIndex, s -> new ArrayList<>())
+ .add(split);
+ });
+ // Assign the splits to their owner fetcher.
+ splitsByFetcherIndex.forEach((fetcherIndex, splitsForFetcher) -> {
+ fetchers.get(fetcherIndex).addSplits(splitsForFetcher);
+ });
+ }
+}
+{% endhighlight %}
+
+
+And a `SourceReader` using this threading model can be created like following:
+
+
+Apparently, the `SourceReader` implementations can also implement their own threading model easily on top of the `SplitFetcherManager` and `SourceReaderBase`.
+
+## Event Time and Watermarks
+
+*Event Time* assignment and *Watermark Generation* happen as part of the data sources. The event streams leaving the Source Readers have event timestamps and (during streaming execution) contain watermarks. See [Timely Stream Processing]({{ site.baseurl }}/concepts/timely-stream-processing.html) for an introduction to Event Time and Watermarks.
+
+Important Applications based on the legacy [SourceFunction](https://github.com/apache/flink/blob/master/flink-streaming-java/src/main/java/org/apache/flink/streaming/api/functions/source/SourceFunction.java) typically generate timestamps and watermarks in a separate later step via `stream.assignTimestampsAndWatermarks(WatermarkStrategy)`. This function should not be used with the new sources, because timestamps will be already assigned, and it will override the previous split-aware watermarks.
+
+#### API
+
+The `WatermarkStrategy` is passed to the Source during creation in the DataStream API and creates both the [TimestampAssigner](https://github.com/apache/flink/blob/master/flink-core/src/main/java/org/apache/flink/api/common/eventtime/TimestampAssigner.java) and [WatermarkGenerator](https://github.com/apache/flink/blob/master/flink-core/src/main/java/org/apache/flink/api/common/eventtime/WatermarkGenerator.java).
+
+{% highlight java %}
+environment.fromSource(
+ Source source,
+ WatermarkStrategy timestampsAndWatermarks,
+ String sourceName)
+{% endhighlight %}
+
+The `TimestampAssigner` and `WatermarkGenerator` run transparently as part of the `ReaderOutput`(or `SourceOutput`) so source implementors do not have to implement any timestamp extraction and watermark generation code.
+
+#### Event Timestamps
+
+Event timestamps are assigned in two steps:
+
+ 1. The SourceReader may attach the *source record timestamp* to the event, by calling `SourceOutput.collect(event, timestamp)`.
+ This is relevant only for data sources that are record-based and have timestamps, such as Kafka, Kinesis, Pulsar, or Pravega.
+ Sources that are not based on records with timestamps (like files) do not have a *source record timestamp*.
+ This step is part of the source connector implementation and not parameterized by the application that uses the source.
+
+ 2. The `TimestampAssigner`, which is configured by the application, assigns the final timestamp.
+ The `TimestampAssigner` sees the original *source record timestamp* and the event. The assigner can use the *source record timestamp* or access a field of the event obtain the final event timestamp.
+
+This two-step approach allows users to reference both timestamps from the source systems and timestamps in the event's data as the event timestamp.
+
+*Note:* When using a data source without *source record timestamps* (like files) and selecting the *source record timestamp* as the final event timestamp, events will get a default timestamp equal to `LONG_MIN` *(=-9,223,372,036,854,775,808)*.
+
+#### Watermark Generation
+
+Watermark Generators are only active during streaming execution. Batch execution deactivates Watermark Generators; all related operations described below become effectively no-ops.
+
+The data source API supports running watermark generators individually *per split*. That allows Flink to observe the event time progress per split individually, which is important to handle *event time skew* properly and prevent *idle partitions* from holding back the event time progress of the entire application.
+
+
+
+
+
+When implementing a source connector using the *Split Reader API*, this is automatically handled. All implementations based on the Split Reader API have split-aware watermarks out-of-the-box.
+
+For an implementation of the lower level `SourceReader` API to use split-aware watermark generation, the implementation must ouput events from different splits to different outputs: the *Split-local SourceOutputs*. Split-local outputs can be created and released on the main [ReaderOutput](https://github.com/apache/flink/blob/master/flink-core/src/main/java/org/apache/flink/api/connector/source/ReaderOutput.java) via the `createOutputForSplit(splitId)` and `releaseOutputForSplit(splitId)` methods. Please refer to the JavaDocs of the class and methods for details.
diff --git a/docs/dev/stream/state/checkpointing.md b/docs/dev/stream/state/checkpointing.md
index c193fc37d71ed5..43dc4b9a94442b 100644
--- a/docs/dev/stream/state/checkpointing.md
+++ b/docs/dev/stream/state/checkpointing.md
@@ -32,7 +32,7 @@ any type of more elaborate operation.
In order to make state fault tolerant, Flink needs to **checkpoint** the state. Checkpoints allow Flink to recover state and positions
in the streams to give the application the same semantics as a failure-free execution.
-The [documentation on streaming fault tolerance]({{ site.baseurl }}/internals/stream_checkpointing.html) describes in detail the technique behind Flink's streaming fault tolerance mechanism.
+The [documentation on streaming fault tolerance]({% link learn-flink/fault_tolerance.md %}) describes in detail the technique behind Flink's streaming fault tolerance mechanism.
## Prerequisites
@@ -72,12 +72,14 @@ Other parameters for checkpointing include:
This option cannot be used when a minimum time between checkpoints is defined.
- - *externalized checkpoints*: You can configure periodic checkpoints to be persisted externally. Externalized checkpoints write their meta data out to persistent storage and are *not* automatically cleaned up when the job fails. This way, you will have a checkpoint around to resume from if your job fails. There are more details in the [deployment notes on externalized checkpoints]({{ site.baseurl }}/ops/state/checkpoints.html#externalized-checkpoints).
+ - *externalized checkpoints*: You can configure periodic checkpoints to be persisted externally. Externalized checkpoints write their meta data out to persistent storage and are *not* automatically cleaned up when the job fails. This way, you will have a checkpoint around to resume from if your job fails. There are more details in the [deployment notes on externalized checkpoints]({% link ops/state/checkpoints.md %}#externalized-checkpoints).
- *fail/continue task on checkpoint errors*: This determines if a task will be failed if an error occurs in the execution of the task's checkpoint procedure. This is the default behaviour. Alternatively, when this is disabled, the task will simply decline the checkpoint to the checkpoint coordinator and continue running.
- *prefer checkpoint for recovery*: This determines if a job will fallback to latest checkpoint even when there are more recent savepoints available to potentially reduce recovery time.
+ - *unaligned checkpoints*: You can enable [unaligned checkpoints]({% link ops/state/checkpoints.md %}#unaligned-checkpoints) to greatly reduce checkpointing times under backpressure. Only works for exactly-once checkpoints and with number of concurrent checkpoints of 1.
+
{% highlight java %}
@@ -105,6 +107,9 @@ env.getCheckpointConfig().enableExternalizedCheckpoints(ExternalizedCheckpointCl
// allow job recovery fallback to checkpoint when there is a more recent savepoint
env.getCheckpointConfig().setPreferCheckpointForRecovery(true);
+
+// enables the experimental unaligned checkpoints
+env.getCheckpointConfig.enableUnalignedCheckpoints();
{% endhighlight %}
@@ -130,6 +135,9 @@ env.getCheckpointConfig.setFailTasksOnCheckpointingErrors(false)
// allow only one checkpoint to be in progress at the same time
env.getCheckpointConfig.setMaxConcurrentCheckpoints(1)
+
+// enables the experimental unaligned checkpoints
+env.getCheckpointConfig.enableUnalignedCheckpoints()
{% endhighlight %}
@@ -158,13 +166,16 @@ env.get_checkpoint_config().enable_externalized_checkpoints(ExternalizedCheckpoi
# allow job recovery fallback to checkpoint when there is a more recent savepoint
env.get_checkpoint_config().set_prefer_checkpoint_for_recovery(True)
+
+// enables the experimental unaligned checkpoints
+env.get_checkpoint_config().enable_unaligned_checkpoints()
{% endhighlight %}
### Related Config Options
-Some more parameters and/or defaults may be set via `conf/flink-conf.yaml` (see [configuration]({{ site.baseurl }}/ops/config.html) for a full guide):
+Some more parameters and/or defaults may be set via `conf/flink-conf.yaml` (see [configuration]({% link ops/config.md %}) for a full guide):
{% include generated/checkpointing_configuration.html %}
@@ -173,7 +184,7 @@ Some more parameters and/or defaults may be set via `conf/flink-conf.yaml` (see
## Selecting a State Backend
-Flink's [checkpointing mechanism]({{ site.baseurl }}/internals/stream_checkpointing.html) stores consistent snapshots
+Flink's [checkpointing mechanism]({% link learn-flink/fault_tolerance.md %}) stores consistent snapshots
of all the state in timers and stateful operators, including connectors, windows, and any [user-defined state](state.html).
Where the checkpoints are stored (e.g., JobManager memory, file system, database) depends on the configured
**State Backend**.
@@ -181,7 +192,7 @@ Where the checkpoints are stored (e.g., JobManager memory, file system, database
By default, state is kept in memory in the TaskManagers and checkpoints are stored in memory in the JobManager. For proper persistence of large state,
Flink supports various approaches for storing and checkpointing state in other state backends. The choice of state backend can be configured via `StreamExecutionEnvironment.setStateBackend(…)`.
-See [state backends]({{ site.baseurl }}/ops/state/state_backends.html) for more details on the available state backends and options for job-wide and cluster-wide configuration.
+See [state backends]({% link ops/state/state_backends.md %}) for more details on the available state backends and options for job-wide and cluster-wide configuration.
## State Checkpoints in Iterative Jobs
@@ -196,7 +207,7 @@ Please note that records in flight in the loop edges (and the state changes asso
## Restart Strategies
Flink supports different restart strategies which control how the jobs are restarted in case of a failure. For more
-information, see [Restart Strategies]({{ site.baseurl }}/dev/restart_strategies.html).
+information, see [Restart Strategies]({% link dev/task_failure_recovery.md %}).
{% top %}
diff --git a/docs/dev/stream/state/checkpointing.zh.md b/docs/dev/stream/state/checkpointing.zh.md
index d4aa989523a680..f9f0592a9e69d1 100644
--- a/docs/dev/stream/state/checkpointing.zh.md
+++ b/docs/dev/stream/state/checkpointing.zh.md
@@ -29,7 +29,7 @@ Flink 中的每个方法或算子都能够是**有状态的**(阅读 [working
状态化的方法在处理单个 元素/事件 的时候存储数据,让状态成为使各个类型的算子更加精细的重要部分。
为了让状态容错,Flink 需要为状态添加 **checkpoint(检查点)**。Checkpoint 使得 Flink 能够恢复状态和在流中的位置,从而向应用提供和无故障执行时一样的语义。
-[容错文档]({{ site.baseurl }}/zh/internals/stream_checkpointing.html) 中介绍了 Flink 流计算容错机制内部的技术原理。
+[容错文档]({{ site.baseurl }}/zh/learn-flink/fault_tolerance.html) 中介绍了 Flink 流计算容错机制内部的技术原理。
## 前提条件
@@ -165,7 +165,7 @@ env.get_checkpoint_config().set_prefer_checkpoint_for_recovery(True)
## 选择一个 State Backend
-Flink 的 [checkpointing 机制]({{ site.baseurl }}/zh/internals/stream_checkpointing.html) 会将 timer 以及 stateful 的 operator 进行快照,然后存储下来,
+Flink 的 [checkpointing 机制]({{ site.baseurl }}/zh/learn-flink/fault_tolerance.html) 会将 timer 以及 stateful 的 operator 进行快照,然后存储下来,
包括连接器(connectors),窗口(windows)以及任何用户[自定义的状态](state.html)。
Checkpoint 存储在哪里取决于所配置的 **State Backend**(比如 JobManager memory、 file system、 database)。
@@ -184,7 +184,7 @@ Flink 现在为没有迭代(iterations)的作业提供一致性的处理保
## 重启策略
-Flink 支持不同的重启策略,来控制 job 万一故障时该如何重启。更多信息请阅读 [重启策略]({{ site.baseurl }}/zh/dev/restart_strategies.html)。
+Flink 支持不同的重启策略,来控制 job 万一故障时该如何重启。更多信息请阅读 [重启策略]({{ site.baseurl }}/zh/dev/task_failure_recovery.html)。
{% top %}
diff --git a/docs/dev/stream/state/custom_serialization.md b/docs/dev/stream/state/custom_serialization.md
index 79f05a36067f14..e7413022476417 100644
--- a/docs/dev/stream/state/custom_serialization.md
+++ b/docs/dev/stream/state/custom_serialization.md
@@ -309,7 +309,7 @@ the nested element serializer.
In these cases, an additional three methods need to be implemented on the `CompositeTypeSerializerSnapshot`:
* `#writeOuterSnapshot(DataOutputView)`: defines how the outer snapshot information is written.
* `#readOuterSnapshot(int, DataInputView, ClassLoader)`: defines how the outer snapshot information is read.
- * `#isOuterSnapshotCompatible(TypeSerializer)`: checks whether the outer snapshot information remains identical.
+ * `#resolveOuterSchemaCompatibility(TypeSerializer)`: checks the compatibility based on the outer snapshot information.
By default, the `CompositeTypeSerializerSnapshot` assumes that there isn't any outer snapshot information to
read / write, and therefore have empty default implementations for the above methods. If the subclass
@@ -351,8 +351,10 @@ public final class GenericArraySerializerSnapshot extends CompositeTypeSerial
}
@Override
- protected boolean isOuterSnapshotCompatible(GenericArraySerializer newSerializer) {
- return this.componentClass == newSerializer.getComponentClass();
+ protected boolean resolveOuterSchemaCompatibility(GenericArraySerializer newSerializer) {
+ return (this.componentClass == newSerializer.getComponentClass())
+ ? OuterSchemaCompatibility.COMPATIBLE_AS_IS
+ : OuterSchemaCompatibility.INCOMPATIBLE;
}
@Override
diff --git a/docs/dev/stream/state/custom_serialization.zh.md b/docs/dev/stream/state/custom_serialization.zh.md
index daeea171d6fd19..2706e8088837d9 100644
--- a/docs/dev/stream/state/custom_serialization.zh.md
+++ b/docs/dev/stream/state/custom_serialization.zh.md
@@ -87,7 +87,7 @@ type and the *serialized binary format* of a state type. The schema, generally s
1. Data schema of the state type has evolved, i.e. adding or removing a field from a POJO that is used as state.
2. Generally speaking, after a change to the data schema, the serialization format of the serializer will need to be upgraded.
3. Configuration of the serializer has changed.
-
+
In order for the new execution to have information about the *written schema* of state and detect whether or not the
schema has changed, upon taking a savepoint of an operator's state, a *snapshot* of the state serializer needs to be
written along with the state bytes. This is abstracted a `TypeSerializerSnapshot`, explained in the next subsection.
@@ -108,10 +108,10 @@ public interface TypeSerializerSnapshot {
{% highlight java %}
-public abstract class TypeSerializer {
-
+public abstract class TypeSerializer {
+
// ...
-
+
public abstract TypeSerializerSnapshot snapshotConfiguration();
}
{% endhighlight %}
@@ -140,7 +140,7 @@ which can be one of the following:
2. **`TypeSerializerSchemaCompatibility.compatibleAfterMigration()`**: this result signals that the new serializer has a
different serialization schema, and it is possible to migrate from the old schema by using the previous serializer
(which recognizes the old schema) to read bytes into state objects, and then rewriting the object back to bytes with
- the new serializer (which recognizes the new schema).
+ the new serializer (which recognizes the new schema).
3. **`TypeSerializerSchemaCompatibility.incompatible()`**: this result signals that the new serializer has a
different serialization schema, but it is not possible to migrate from the old schema.
@@ -170,13 +170,13 @@ to the implementation of state serializers and their serializer snapshots.
- Upon receiving the new serializer, it is provided to the restored previous serializer's snapshot via the
`TypeSerializer#resolveSchemaCompatibility` to check for schema compatibility.
4. **Migrate state bytes in backend from schema _A_ to schema _B_**
- - If the compatibility resolution reflects that the schema has changed and migration is possible, schema migration is
+ - If the compatibility resolution reflects that the schema has changed and migration is possible, schema migration is
performed. The previous state serializer which recognizes schema _A_ will be obtained from the serializer snapshot, via
`TypeSerializerSnapshot#restoreSerializer()`, and is used to deserialize state bytes to objects, which in turn
are re-written again with the new serializer, which recognizes schema _B_ to complete the migration. All entries
of the accessed state is migrated all-together before processing continues.
- If the resolution signals incompatibility, then the state access fails with an exception.
-
+
#### Heap state backends (e.g. `MemoryStateBackend`, `FsStateBackend`)
1. **Register new state with a state serializer that has schema _A_**
@@ -218,7 +218,7 @@ as your serializer's snapshot class:
- `TypeSerializerSchemaCompatibility.compatibleAsIs()`, if the new serializer class remains identical, or
- `TypeSerializerSchemaCompatibility.incompatible()`, if the new serializer class is different then the previous one.
-
+
Below is an example of how the `SimpleTypeSerializerSnapshot` is used, using Flink's `IntSerializer` as an example:
{% highlight java %}
@@ -309,7 +309,7 @@ the nested element serializer.
In these cases, an additional three methods need to be implemented on the `CompositeTypeSerializerSnapshot`:
* `#writeOuterSnapshot(DataOutputView)`: defines how the outer snapshot information is written.
* `#readOuterSnapshot(int, DataInputView, ClassLoader)`: defines how the outer snapshot information is read.
- * `#isOuterSnapshotCompatible(TypeSerializer)`: checks whether the outer snapshot information remains identical.
+ * `#resolveOuterSchemaCompatibility(TypeSerializer)`: checks the compatibility based on the outer snapshot information.
By default, the `CompositeTypeSerializerSnapshot` assumes that there isn't any outer snapshot information to
read / write, and therefore have empty default implementations for the above methods. If the subclass
@@ -351,8 +351,10 @@ public final class GenericArraySerializerSnapshot extends CompositeTypeSerial
}
@Override
- protected boolean isOuterSnapshotCompatible(GenericArraySerializer newSerializer) {
- return this.componentClass == newSerializer.getComponentClass();
+ protected boolean resolveOuterSchemaCompatibility(GenericArraySerializer newSerializer) {
+ return (this.componentClass == newSerializer.getComponentClass())
+ ? OuterSchemaCompatibility.COMPATIBLE_AS_IS
+ : OuterSchemaCompatibility.INCOMPATIBLE;
}
@Override
diff --git a/docs/dev/stream/state/index.zh.md b/docs/dev/stream/state/index.zh.md
index 1b5444873a0a28..97e2d97a4022d9 100644
--- a/docs/dev/stream/state/index.zh.md
+++ b/docs/dev/stream/state/index.zh.md
@@ -25,34 +25,17 @@ specific language governing permissions and limitations
under the License.
-->
-Stateful functions and operators store data across the processing of individual elements/events, making state a critical building block for
-any type of more elaborate operation.
-
-For example:
-
- - When an application searches for certain event patterns, the state will store the sequence of events encountered so far.
- - When aggregating events per minute/hour/day, the state holds the pending aggregates.
- - When training a machine learning model over a stream of data points, the state holds the current version of the model parameters.
- - When historic data needs to be managed, the state allows efficient access to events that occurred in the past.
-
-Flink needs to be aware of the state in order to make state fault tolerant using [checkpoints](checkpointing.html) and to allow [savepoints]({{ site.baseurl }}/ops/state/savepoints.html) of streaming applications.
-
-Knowledge about the state also allows for rescaling Flink applications, meaning that Flink takes care of redistributing state across parallel instances.
-
-The [queryable state](queryable_state.html) feature of Flink allows you to access state from outside of Flink during runtime.
-
-When working with state, it might also be useful to read about [Flink's state backends]({{ site.baseurl }}/ops/state/state_backends.html). Flink provides different state backends that specify how and where state is stored. State can be located on Java's heap or off-heap. Depending on your state backend, Flink can also *manage* the state for the application, meaning Flink deals with the memory management (possibly spilling to disk if necessary) to allow applications to hold very large state. State backends can be configured without changing your application logic.
-
+你将在本节中了解到 Flink 提供的用于编写有状态程序的 API,想了解更多有状态流处理的概念,请查看[有状态的流处理]({% link concepts/stateful-stream-processing.zh.md %})
{% top %}
-Where to go next?
+接下来看什么?
-----------------
-* [Working with State](state.html): Shows how to use state in a Flink application and explains the different kinds of state.
-* [The Broadcast State Pattern](broadcast_state.html): Explains how to connect a broadcast stream with a non-broadcast stream and use state to exchange information between them.
-* [Checkpointing](checkpointing.html): Describes how to enable and configure checkpointing for fault tolerance.
-* [Queryable State](queryable_state.html): Explains how to access state from outside of Flink during runtime.
-* [State Schema Evolution](schema_evolution.html): Shows how schema of state types can be evolved.
-* [Custom Serialization for Managed State](custom_serialization.html): Discusses how to implement custom serializers, especially for schema evolution.
+* [Working with State](state.html): 描述了如何在 Flink 应用程序中使用状态,以及不同类型的状态。
+* [The Broadcast State 模式](broadcast_state.html): 描述了如何将广播流和非广播流进行连接从而交换数据。
+* [Checkpointing](checkpointing.html): 介绍了如何开启和配置 checkpoint,以实现状态容错。
+* [Queryable State](queryable_state.html): 介绍了如何从外围访问 Flink 的状态。
+* [状态数据结构升级](schema_evolution.html): 介绍了状态数据结构升级相关的内容。
+* [Managed State 的自定义序列化器](custom_serialization.html): 介绍了如何实现自定义的序列化器,尤其是如何支持状态数据结构升级。
{% top %}
diff --git a/docs/dev/stream/state/queryable_state.md b/docs/dev/stream/state/queryable_state.md
index d4c287921f235e..03a24729c90dc5 100644
--- a/docs/dev/stream/state/queryable_state.md
+++ b/docs/dev/stream/state/queryable_state.md
@@ -180,7 +180,7 @@ jar which must be explicitly included as a dependency in the `pom.xml` of your p
{% endhighlight %}
-For more on this, you can check how to [set up a Flink program]({{ site.baseurl }}/dev/projectsetup/dependencies.html).
+For more on this, you can check how to [set up a Flink program]({{ site.baseurl }}/dev/project-configuration.html).
The `QueryableStateClient` will submit your query to the internal proxy, which will then process your query and return
the final result. The only requirement to initialize the client is to provide a valid `TaskManager` hostname (remember
diff --git a/docs/dev/stream/state/queryable_state.zh.md b/docs/dev/stream/state/queryable_state.zh.md
index 3c14c7e8360a92..4b76b4d40f6c25 100644
--- a/docs/dev/stream/state/queryable_state.zh.md
+++ b/docs/dev/stream/state/queryable_state.zh.md
@@ -1,5 +1,5 @@
---
-title: "可查询状态"
+title: "Queryable State"
nav-parent_id: streaming_state
nav-pos: 4
is_beta: true
@@ -27,75 +27,52 @@ under the License.
{:toc}
- Note: The client APIs for queryable state are currently in an evolving state and
- there are no guarantees made about stability of the provided interfaces. It is
- likely that there will be breaking API changes on the client side in the upcoming Flink versions.
+ 注意: 目前 querable state 的客户端 API 还在不断演进,不保证现有接口的稳定性。在后续的 Flink 版本中有可能发生 API 变化。
-In a nutshell, this feature exposes Flink's managed keyed (partitioned) state
-(see [Working with State]({{ site.baseurl }}/dev/stream/state/state.html)) to the outside world and
-allows the user to query a job's state from outside Flink. For some scenarios, queryable state
-eliminates the need for distributed operations/transactions with external systems such as key-value
-stores which are often the bottleneck in practice. In addition, this feature may be particularly
-useful for debugging purposes.
+简而言之, 这个特性将 Flink 的 managed keyed (partitioned) state
+(参考 [Working with State]({{ site.baseurl }}/zh/dev/stream/state/state.html)) 暴露给外部,从而用户可以在 Flink 外部查询作业 state。
+在某些场景中,Queryable State 消除了对外部系统的分布式操作以及事务的需求,比如 KV 存储系统,而这些外部系统往往会成为瓶颈。除此之外,这个特性对于调试作业非常有用。
- Attention: When querying a state object, that object is accessed from a concurrent
- thread without any synchronization or copying. This is a design choice, as any of the above would lead
- to increased job latency, which we wanted to avoid. Since any state backend using Java heap space,
- e.g.MemoryStateBackend or FsStateBackend, does not work
- with copies when retrieving values but instead directly references the stored values, read-modify-write
- patterns are unsafe and may cause the queryable state server to fail due to concurrent modifications.
- The RocksDBStateBackend is safe from these issues.
+ 注意: 进行查询时,state 会在并发线程中被访问,但 state 不会进行同步和拷贝。这种设计是为了避免同步和拷贝带来的作业延时。对于使用 Java 堆内存的 state backend,
+ 比如MemoryStateBackend 或者 FsStateBackend,它们获取状态时不会进行拷贝,而是直接引用状态对象,所以对状态的 read-modify-write 是不安全的,并且可能会因为并发修改导致查询失败。但 RocksDBStateBackend 是安全的,不会遇到上述问题。
-## Architecture
+## 架构
-Before showing how to use the Queryable State, it is useful to briefly describe the entities that compose it.
-The Queryable State feature consists of three main entities:
+在展示如何使用 Queryable State 之前,先简单描述一下该特性的组成部分,主要包括以下三部分:
- 1. the `QueryableStateClient`, which (potentially) runs outside the Flink cluster and submits the user queries,
- 2. the `QueryableStateClientProxy`, which runs on each `TaskManager` (*i.e.* inside the Flink cluster) and is responsible
- for receiving the client's queries, fetching the requested state from the responsible Task Manager on his behalf, and
- returning it to the client, and
- 3. the `QueryableStateServer` which runs on each `TaskManager` and is responsible for serving the locally stored state.
+ 1. `QueryableStateClient`,默认运行在 Flink 集群外部,负责提交用户的查询请求;
+ 2. `QueryableStateClientProxy`,运行在每个 `TaskManager` 上(*即* Flink 集群内部),负责接收客户端的查询请求,从所负责的 Task Manager 获取请求的 state,并返回给客户端;
+ 3. `QueryableStateServer`, 运行在 `TaskManager` 上,负责服务本地存储的 state。
-The client connects to one of the proxies and sends a request for the state associated with a specific
-key, `k`. As stated in [Working with State]({{ site.baseurl }}/dev/stream/state/state.html), keyed state is organized in
-*Key Groups*, and each `TaskManager` is assigned a number of these key groups. To discover which `TaskManager` is
-responsible for the key group holding `k`, the proxy will ask the `JobManager`. Based on the answer, the proxy will
-then query the `QueryableStateServer` running on that `TaskManager` for the state associated with `k`, and forward the
-response back to the client.
+客户端连接到一个代理,并发送请求获取特定 `k` 对应的 state。 如 [Working with State]({{ site.baseurl }}/zh/dev/stream/state/state.html) 所述,keyed state 按照
+*Key Groups* 进行划分,每个 `TaskManager` 会分配其中的一些 key groups。代理会询问 `JobManager` 以找到 `k` 所属 key group 的 TaskManager。根据返回的结果, 代理将会向运行在 `TaskManager` 上的 `QueryableStateServer` 查询 `k` 对应的 state, 并将结果返回给客户端。
-## Activating Queryable State
+## 激活 Queryable State
-To enable queryable state on your Flink cluster, you need to do the following:
+为了在 Flink 集群上使用 queryable state,需要进行以下操作:
- 1. copy the `flink-queryable-state-runtime{{ site.scala_version_suffix }}-{{site.version }}.jar`
-from the `opt/` folder of your [Flink distribution](https://flink.apache.org/downloads.html "Apache Flink: Downloads"),
-to the `lib/` folder.
- 2. set the property `queryable-state.enable` to `true`. See the [Configuration]({{ site.baseurl }}/ops/config.html#queryable-state) documentation for details and additional parameters.
+ 1. 将 `flink-queryable-state-runtime{{ site.scala_version_suffix }}-{{site.version }}.jar`
+从 [Flink distribution](https://flink.apache.org/downloads.html "Apache Flink: Downloads") 的 `opt/` 目录拷贝到 `lib/` 目录;
+ 2. 将参数 `queryable-state.enable` 设置为 `true`。详细信息以及其它配置可参考文档 [Configuration]({{ site.baseurl }}/zh/ops/config.html#queryable-state)。
-To verify that your cluster is running with queryable state enabled, check the logs of any
-task manager for the line: `"Started the Queryable State Proxy Server @ ..."`.
+为了验证集群的 queryable state 已经被激活,可以检查任意 task manager 的日志中是否包含 "Started the Queryable State Proxy Server @ ..."。
-## Making State Queryable
+## 将 state 设置为可查询的
-Now that you have activated queryable state on your cluster, it is time to see how to use it. In order for a state to
-be visible to the outside world, it needs to be explicitly made queryable by using:
+激活集群的 queryable state 功能后,还要将 state 设置为可查询的才能对外可见,可以通过以下两种方式进行设置:
-* either a `QueryableStateStream`, a convenience object which acts as a sink and offers its incoming values as queryable
-state, or
-* the `stateDescriptor.setQueryable(String queryableStateName)` method, which makes the keyed state represented by the
- state descriptor, queryable.
+* 创建 `QueryableStateStream`,它会作为一个 sink,并将输入数据转化为 queryable state;
+* 通过 `stateDescriptor.setQueryable(String queryableStateName)` 将 state 描述符所表示的 keyed state 设置成可查询的。
-The following sections explain the use of these two approaches.
+接下来的部分将详细解释这两种方式。
### Queryable State Stream
-Calling `.asQueryableState(stateName, stateDescriptor)` on a `KeyedStream` returns a `QueryableStateStream` which offers
-its values as queryable state. Depending on the type of state, there are the following variants of the `asQueryableState()`
-method:
+在 `KeyedStream` 上调用 `.asQueryableState(stateName, stateDescriptor)` 将会返回一个 `QueryableStateStream`, 它会将流数据转化为 queryable state。
+对应不同的 state 类型,`asQueryableState()` 有以下一些方法变体:
{% highlight java %}
// ValueState
@@ -119,28 +96,23 @@ QueryableStateStream asQueryableState(
- Note: There is no queryable ListState sink as it would result in an ever-growing
- list which may not be cleaned up and thus will eventually consume too much memory.
+ 注意: 没有可查询的 ListState sink,因为这种情况下 list 会不断增长,并且可能不会被清理,最终会消耗大量的内存。
-The returned `QueryableStateStream` can be seen as a sink and **cannot** be further transformed. Internally, a
-`QueryableStateStream` gets translated to an operator which uses all incoming records to update the queryable state
-instance. The updating logic is implied by the type of the `StateDescriptor` provided in the `asQueryableState` call.
-In a program like the following, all records of the keyed stream will be used to update the state instance via the
-`ValueState.update(value)`:
+返回的 `QueryableStateStream` 可以被视作一个sink,而且**不能再**被进一步转换。在内部实现上,一个 `QueryableStateStream` 被转换成一个 operator,使用输入的数据来更新 queryable state。state 如何更新是由 `asQueryableState` 提供的 `StateDescriptor` 来决定的。在下面的代码中, keyed stream 的所有数据将会通过 `ValueState.update(value)` 来更新状态:
{% highlight java %}
stream.keyBy(0).asQueryableState("query-name")
{% endhighlight %}
-This acts like the Scala API's `flatMapWithState`.
+这个行为类似于 Scala API 中的 `flatMapWithState`。
### Managed Keyed State
-Managed keyed state of an operator
-(see [Using Managed Keyed State]({{ site.baseurl }}/dev/stream/state/state.html#using-managed-keyed-state))
-can be made queryable by making the appropriate state descriptor queryable via
-`StateDescriptor.setQueryable(String queryableStateName)`, as in the example below:
+operator 中的 Managed keyed state
+(参考 [Using Managed Keyed State]({{ site.baseurl }}/zh/dev/stream/state/state.html#using-managed-keyed-state))
+可以通过 `StateDescriptor.setQueryable(String queryableStateName)` 将 state descriptor 设置成可查询的,从而使 state 可查询,如下所示:
+
{% highlight java %}
ValueStateDescriptor> descriptor =
new ValueStateDescriptor<>(
@@ -150,20 +122,17 @@ descriptor.setQueryable("query-name"); // queryable state name
{% endhighlight %}
- Note: The queryableStateName parameter may be chosen arbitrarily and is only
- used for queries. It does not have to be identical to the state's own name.
+ 注意: 参数 queryableStateName 可以任意选取,并且只被用来进行查询,它可以和 state 的名称不同。
-This variant has no limitations as to which type of state can be made queryable. This means that this can be used for
-any `ValueState`, `ReduceState`, `ListState`, `MapState`, `AggregatingState`, and the currently deprecated `FoldingState`.
+这种方式不会限制 state 类型,即任意的 `ValueState`、`ReduceState`、`ListState`、`MapState`、`AggregatingState` 以及已弃用的 `FoldingState`
+均可作为 queryable state。
-## Querying State
+## 查询 state
-So far, you have set up your cluster to run with queryable state and you have declared (some of) your state as
-queryable. Now it is time to see how to query this state.
+目前为止,你已经激活了集群的 queryable state 功能,并且将一些 state 设置成了可查询的,接下来将会展示如何进行查询。
-For this you can use the `QueryableStateClient` helper class. This is available in the `flink-queryable-state-client`
-jar which must be explicitly included as a dependency in the `pom.xml` of your project along with `flink-core`, as shown below:
+为了进行查询,可以使用辅助类 `QueryableStateClient`,这个类位于 `flink-queryable-state-client` 的 jar 中,在项目的 `pom.xml` 需要显示添加对 `flink-queryable-state-client` 和 `flink-core` 的依赖, 如下所示:
{% highlight xml %}
@@ -180,18 +149,16 @@ jar which must be explicitly included as a dependency in the `pom.xml` of your p
{% endhighlight %}
-For more on this, you can check how to [set up a Flink program]({{ site.baseurl }}/dev/projectsetup/dependencies.html).
+关于依赖的更多信息, 可以参考如何 [配置 Flink 项目]({{ site.baseurl }}/zh/dev/project-configuration.html).
-The `QueryableStateClient` will submit your query to the internal proxy, which will then process your query and return
-the final result. The only requirement to initialize the client is to provide a valid `TaskManager` hostname (remember
-that there is a queryable state proxy running on each task manager) and the port where the proxy listens. More on how
-to configure the proxy and state server port(s) in the [Configuration Section](#configuration).
+`QueryableStateClient` 将提交你的请求到内部代理,代理会处理请求并返回结果。客户端的初始化只需要提供一个有效的 `TaskManager` 主机名
+(每个 task manager 上都运行着一个 queryable state 代理),以及代理监听的端口号。关于如何配置代理以及端口号可以参考 [Configuration Section](#configuration).
{% highlight java %}
QueryableStateClient client = new QueryableStateClient(tmHostname, proxyPort);
{% endhighlight %}
-With the client ready, to query a state of type `V`, associated with a key of type `K`, you can use the method:
+客户端就绪后,为了查询类型为 `K` 的 key,以及类型为 `V` 的state,可以使用如下方法:
{% highlight java %}
CompletableFuture getKvState(
@@ -202,35 +169,29 @@ CompletableFuture getKvState(
StateDescriptor stateDescriptor)
{% endhighlight %}
-The above returns a `CompletableFuture` eventually holding the state value for the queryable state instance identified
-by `queryableStateName` of the job with ID `jobID`. The `key` is the key whose state you are interested in and the
-`keyTypeInfo` will tell Flink how to serialize/deserialize it. Finally, the `stateDescriptor` contains the necessary
-information about the requested state, namely its type (`Value`, `Reduce`, etc) and the necessary information on how
-to serialize/deserialize it.
+该方法会返回一个最终将包含 state 的 queryable state 实例,该实例可通过 JobID 和 queryableStateName 识别。在方法参数中,`key` 用来指定所要查询的状态所属的 key。
+`keyTypeInfo` 告诉 Flink 如何对 key 进行序列化和反序列化。`stateDescriptor` 包含了所请求 state 的必要信息,即 state 类型(`Value`,`Reduce` 等等),
+以及如何对其进行序列化和反序列。
-The careful reader will notice that the returned future contains a value of type `S`, *i.e.* a `State` object containing
-the actual value. This can be any of the state types supported by Flink: `ValueState`, `ReduceState`, `ListState`, `MapState`,
-`AggregatingState`, and the currently deprecated `FoldingState`.
+细心的读者会注意到返回的 future 包含类型为 `S` 的值,*即*一个存储实际值的 `State` 对象。它可以是Flink支持的任何类型的 state:`ValueState`、`ReduceState`、
+`ListState`、`MapState`、`AggregatingState` 以及弃用的 `FoldingState`。
- Note: These state objects do not allow modifications to the contained state. You can use them to get
- the actual value of the state, e.g. using valueState.get(), or iterate over
- the contained entries, e.g. using the mapState.entries(), but you cannot
- modify them. As an example, calling the add() method on a returned list state will throw an
- UnsupportedOperationException.
+ 注意: 这些 state 对象不允许对其中的 state 进行修改。你可以通过 valueState.get() 获取实际的 state,
+ 或者通过 mapState.entries() 遍历所有 ,但是不能修改它们。举例来说,对返回的 list state 调用 add()
+ 方法将会导致 UnsupportedOperationException。
- Note: The client is asynchronous and can be shared by multiple threads. It needs
- to be shutdown via QueryableStateClient.shutdown() when unused in order to free
- resources.
+ 注意: 客户端是异步的,并且可能被多个线程共享。客户端不再使用后需要通过 QueryableStateClient.shutdown()
+ 来终止,从而释放资源。
-### Example
+### 示例
-The following example extends the `CountWindowAverage` example
-(see [Using Managed Keyed State]({{ site.baseurl }}/dev/stream/state/state.html#using-managed-keyed-state))
-by making it queryable and shows how to query this value:
+下面的例子扩展自 `CountWindowAverage`
+(参考 [Using Managed Keyed State]({{ site.baseurl }}/zh/dev/stream/state/state.html#using-managed-keyed-state)),
+将其中的 state 设置成可查询的,并展示了如何进行查询:
{% highlight java %}
public class CountWindowAverage extends RichFlatMapFunction, Tuple2> {
@@ -262,7 +223,7 @@ public class CountWindowAverage extends RichFlatMapFunction,
}
{% endhighlight %}
-Once used in a job, you can retrieve the job ID and then query any key's current state from this operator:
+上面的代码作为作业运行后,可以获取作业的 ID,然后可以通过下面的方式查询任何 key 下的 state。
{% highlight java %}
QueryableStateClient client = new QueryableStateClient(tmHostname, proxyPort);
@@ -288,34 +249,26 @@ resultFuture.thenAccept(response -> {
## Configuration
-The following configuration parameters influence the behaviour of the queryable state server and client.
-They are defined in `QueryableStateOptions`.
+下面的配置会影响 queryable state 服务器端和客户端的行为,它们定义在 `QueryableStateOptions`。
### State Server
-* `queryable-state.server.ports`: the server port range of the queryable state server. This is useful to avoid port clashes if more
- than 1 task managers run on the same machine. The specified range can be: a port: "9123", a range of ports: "50100-50200",
- or a list of ranges and or points: "50100-50200,50300-50400,51234". The default port is 9067.
-* `queryable-state.server.network-threads`: number of network (event loop) threads receiving incoming requests for the state server (0 => #slots)
-* `queryable-state.server.query-threads`: number of threads handling/serving incoming requests for the state server (0 => #slots).
+* `queryable-state.server.ports`: 服务器端口范围,如果同一台机器上运行了多个 task manager,可以避免端口冲突。指定的可以是一个具体的端口号,如 "9123",
+ 可以是一个端口范围,如 "50100-50200",或者可以是端口范围以及端口号的组合,如 "50100-50200,50300-50400,51234"。默认端口号是 9067。
+* `queryable-state.server.network-threads`: 服务器端 network (event loop) thread 的数量,用来接收查询请求 (如果设置为0,则线程数为 slot 数)。
+* `queryable-state.server.query-threads`: 服务器端处理查询请求的线程数 (如果设置为0,则线程数为 slot 数)。
### Proxy
-* `queryable-state.proxy.ports`: the server port range of the queryable state proxy. This is useful to avoid port clashes if more
- than 1 task managers run on the same machine. The specified range can be: a port: "9123", a range of ports: "50100-50200",
- or a list of ranges and or points: "50100-50200,50300-50400,51234". The default port is 9069.
-* `queryable-state.proxy.network-threads`: number of network (event loop) threads receiving incoming requests for the client proxy (0 => #slots)
-* `queryable-state.proxy.query-threads`: number of threads handling/serving incoming requests for the client proxy (0 => #slots).
-
-## Limitations
-
-* The queryable state life-cycle is bound to the life-cycle of the job, *e.g.* tasks register
-queryable state on startup and unregister it on disposal. In future versions, it is desirable to
-decouple this in order to allow queries after a task finishes, and to speed up recovery via state
-replication.
-* Notifications about available KvState happen via a simple tell. In the future this should be improved to be
-more robust with asks and acknowledgements.
-* The server and client keep track of statistics for queries. These are currently disabled by
-default as they would not be exposed anywhere. As soon as there is better support to publish these
-numbers via the Metrics system, we should enable the stats.
+* `queryable-state.proxy.ports`: 代理的服务端口范围。如果同一台机器上运行了多个 task manager,可以避免端口冲突。指定的可以是一个具体的端口号,如 "9123",
+ 可以是一个端口范围,如"50100-50200",或者可以是端口范围以及端口号的组合,如 "50100-50200,50300-50400,51234"。默认端口号是 9069。
+* `queryable-state.proxy.network-threads`: 代理上 network (event loop) thread 的数量,用来接收查询请求 (如果设置为0,则线程数为 slot 数)。
+* `queryable-state.proxy.query-threads`: 代理上处理查询请求的线程数 (如果设置为0,则线程数为 slot 数)。
+
+## 限制
+
+* queryable state 的生命周期受限于作业的生命周期,*比如* tasks 在启动时注册可查询状态,并在退出时注销。在后续版本中,希望能够将其解耦
+从而允许 task 结束后依然能够查询 state,并且通过 state 备份来加速恢复。
+* 目前是通过 tell 来通知可用的 KvState。将来会使用 asks 和 acknowledgements 来提升稳定性。
+* 服务器端和客户端会记录请求的统计信息。因为统计信息目前不会暴露给外部,所以这个功能默认没有开启。如果将来支持通过 Metrics 系统发布这些数据,将开启统计功能。
{% top %}
diff --git a/docs/dev/stream/state/state.md b/docs/dev/stream/state/state.md
index 728beab71163b5..389a03e4fc234d 100644
--- a/docs/dev/stream/state/state.md
+++ b/docs/dev/stream/state/state.md
@@ -115,11 +115,6 @@ added to the state. Contrary to `ReducingState`, the aggregate type may be diffe
of elements that are added to the state. The interface is the same as for `ListState` but elements
added using `add(IN)` are aggregated using a specified `AggregateFunction`.
-* `FoldingState`: This keeps a single value that represents the aggregation of all values
-added to the state. Contrary to `ReducingState`, the aggregate type may be different from the type
-of elements that are added to the state. The interface is similar to `ListState` but elements
-added using `add(T)` are folded into an aggregate using a specified `FoldFunction`.
-
* `MapState`: This keeps a list of mappings. You can put key-value pairs into the state and
retrieve an `Iterable` over all currently stored mappings. Mappings are added using `put(UK, UV)` or
`putAll(Map)`. The value associated with a user key can be retrieved using `get(UK)`. The iterable
@@ -129,8 +124,6 @@ You can also use `isEmpty()` to check whether this map contains any key-value ma
All types of state also have a method `clear()` that clears the state for the currently
active key, i.e. the key of the input element.
-Attention `FoldingState` and `FoldingStateDescriptor` have been deprecated in Flink 1.4 and will be completely removed in the future. Please use `AggregatingState` and `AggregatingStateDescriptor` instead.
-
It is important to keep in mind that these state objects are only used for interfacing
with state. The state is not necessarily stored inside but might reside on disk or somewhere else.
The second thing to keep in mind is that the value you get from the state
@@ -142,7 +135,7 @@ To get a state handle, you have to create a `StateDescriptor`. This holds the na
that you can reference them), the type of the values that the state holds, and possibly
a user-specified function, such as a `ReduceFunction`. Depending on what type of state you
want to retrieve, you create either a `ValueStateDescriptor`, a `ListStateDescriptor`,
-a `ReducingStateDescriptor`, a `FoldingStateDescriptor` or a `MapStateDescriptor`.
+a `ReducingStateDescriptor`, or a `MapStateDescriptor`.
State is accessed using the `RuntimeContext`, so it is only possible in *rich functions*.
Please see [here]({% link dev/user_defined_functions.md %}#rich-functions) for
@@ -153,7 +146,6 @@ is available in a `RichFunction` has these methods for accessing state:
* `ReducingState getReducingState(ReducingStateDescriptor)`
* `ListState getListState(ListStateDescriptor)`
* `AggregatingState getAggregatingState(AggregatingStateDescriptor)`
-* `FoldingState getFoldingState(FoldingStateDescriptor)`
* `MapState getMapState(MapStateDescriptor)`
This is an example `FlatMapFunction` that shows how all of the parts fit together:
diff --git a/docs/dev/stream/state/state.zh.md b/docs/dev/stream/state/state.zh.md
index dad89a35fcd6ce..16e5e9646ed07f 100644
--- a/docs/dev/stream/state/state.zh.md
+++ b/docs/dev/stream/state/state.zh.md
@@ -22,51 +22,75 @@ specific language governing permissions and limitations
under the License.
-->
-本文档主要介绍如何在 Flink 作业中使用状态
+In this section you will learn about the APIs that Flink provides for writing
+stateful programs. Please take a look at [Stateful Stream
+Processing]({% link concepts/stateful-stream-processing.zh.md %})
+to learn about the concepts behind stateful stream processing.
+
* 目录
{:toc}
-## Keyed State 与 Operator State
-
-Flink 中有两种基本的状态:`Keyed State` 和 `Operator State`。
-
-### Keyed State
-
-*Keyed State* 通常和 key 相关,仅可使用在 `KeyedStream` 的方法和算子中。
+## Keyed DataStream
-你可以把 Keyed State 看作分区或者共享的 Operator State, 而且每个 key 仅出现在一个分区内。
-逻辑上每个 keyed-state 和唯一元组 <算子并发实例, key> 绑定,由于每个 key 仅"属于"
-算子的一个并发,因此简化为 <算子, key>。
+If you want to use keyed state, you first need to specify a key on a
+`DataStream` that should be used to partition the state (and also the records
+in the stream themselves). You can specify a key using `keyBy(KeySelector)` on
+a `DataStream`. This will yield a `KeyedDataStream`, which then allows
+operations that use keyed state.
-Keyed State 会按照 *Key Group* 进行管理。Key Group 是 Flink 分发 Keyed State 的最小单元;
-Key Group 的数目等于作业的最大并发数。在执行过程中,每个 keyed operator 会对应到一个或多个 Key Group
+A key selector function takes a single record as input and returns the key for
+that record. The key can be of any type and **must** be derived from
+deterministic computations.
-### Operator State
+The data model of Flink is not based on key-value pairs. Therefore, you do not
+need to physically pack the data set types into keys and values. Keys are
+"virtual": they are defined as functions over the actual data to guide the
+grouping operator.
-对于 *Operator State* (或者 *non-keyed state*) 来说,每个 operator state 和一个并发实例进行绑定。
-[Kafka Connector]({{ site.baseurl }}/zh/dev/connectors/kafka.html) 是 Flink 中使用 operator state 的一个很好的示例。
-每个 Kafka 消费者的并发在 Operator State 中维护一个 topic partition 到 offset 的映射关系。
+The following example shows a key selector function that simply returns the
+field of an object:
-Operator State 在 Flink 作业的并发改变后,会重新分发状态,分发的策略和 Keyed State 不一样。
-
-## Raw State 与 Managed State
+
+
+{% highlight java %}
+// some ordinary POJO
+public class WC {
+ public String word;
+ public int count;
-*Keyed State* 和 *Operator State* 分别有两种存在形式:*managed* and *raw*.
+ public String getWord() { return word; }
+}
+DataStream words = // [...]
+KeyedStream keyed = words
+ .keyBy(WC::getWord);
+{% endhighlight %}
-*Managed State* 由 Flink 运行时控制的数据结构表示,比如内部的 hash table 或者 RocksDB。
-比如 "ValueState", "ListState" 等。Flink runtime 会对这些状态进行编码并写入 checkpoint。
+
+
+{% highlight scala %}
+// some ordinary case class
+case class WC(word: String, count: Int)
+val words: DataStream[WC] = // [...]
+val keyed = words.keyBy( _.word )
+{% endhighlight %}
+
+
-*Raw State* 则保存在算子自己的数据结构中。checkpoint 的时候,Flink 并不知晓具体的内容,仅仅写入一串字节序列到 checkpoint。
+### Tuple Keys and Expression Keys
+{:.no_toc}
-所有 datastream 的 function 都可以使用 managed state, 但是 raw state 则只能在实现算子的时候使用。
-由于 Flink 可以在修改并发时更好的分发状态数据,并且能够更好的管理内存,因此建议使用 managed state(而不是 raw state)。
+Flink also has two alternative ways of defining keys: tuple keys and expression
+keys. With this you can specify keys using tuple field indices or expressions
+for selecting fields of objects. We don't recommend using these today but you
+can refer to the Javadoc of DataStream to learn about them. Using a KeySelector
+function is strictly superior: with Java lambdas they are easy to use and they
+have potentially less overhead at runtime.
-注意 如果你的 managed state 需要定制化的序列化逻辑,
-为了后续的兼容性请参考 [相应指南](custom_serialization.html),Flink 的默认序列化器不需要用户做特殊的处理。
+{% top %}
-## 使用 Managed Keyed State
+## 使用 Keyed State
-managed keyed state 接口提供不同类型状态的访问接口,这些状态都作用于当前输入数据的 key 下。换句话说,这些状态仅可在 `KeyedStream`
+keyed state 接口提供不同类型状态的访问接口,这些状态都作用于当前输入数据的 key 下。换句话说,这些状态仅可在 `KeyedStream`
上使用,可以通过 `stream.keyBy(...)` 得到 `KeyedStream`.
接下来,我们会介绍不同类型的状态,然后介绍如何使用他们。所有支持的状态类型如下所示:
@@ -83,32 +107,25 @@ managed keyed state 接口提供不同类型状态的访问接口,这些状态
* `AggregatingState`: 保留一个单值,表示添加到状态的所有值的聚合。和 `ReducingState` 相反的是, 聚合类型可能与 添加到状态的元素的类型不同。
接口与 `ListState` 类似,但使用 `add(IN)` 添加的元素会用指定的 `AggregateFunction` 进行聚合。
-* `FoldingState`: 保留一个单值,表示添加到状态的所有值的聚合。 与 `ReducingState` 相反,聚合类型可能与添加到状态的元素类型不同。
-接口与 `ListState` 类似,但使用`add(T)`添加的元素会用指定的 `FoldFunction` 折叠成聚合值。
-
* `MapState`: 维护了一个映射列表。 你可以添加键值对到状态中,也可以获得反映当前所有映射的迭代器。使用 `put(UK,UV)` 或者 `putAll(Map)` 添加映射。
使用 `get(UK)` 检索特定 key。 使用 `entries()`,`keys()` 和 `values()` 分别检索映射、键和值的可迭代视图。你还可以通过 `isEmpty()` 来判断是否包含任何键值对。
所有类型的状态还有一个`clear()` 方法,清除当前 key 下的状态数据,也就是当前输入元素的 key。
-注意 `FoldingState` 和 `FoldingStateDescriptor` 从 Flink 1.4 开始就已经被启用,将会在未来被删除。
-作为替代请使用 `AggregatingState` 和 `AggregatingStateDescriptor`。
-
请牢记,这些状态对象仅用于与状态交互。状态本身不一定存储在内存中,还可能在磁盘或其他位置。
另外需要牢记的是从状态中获取的值取决于输入元素所代表的 key。 因此,在不同 key 上调用同一个接口,可能得到不同的值。
你必须创建一个 `StateDescriptor`,才能得到对应的状态句柄。 这保存了状态名称(正如我们稍后将看到的,你可以创建多个状态,并且它们必须具有唯一的名称以便可以引用它们),
状态所持有值的类型,并且可能包含用户指定的函数,例如`ReduceFunction`。 根据不同的状态类型,可以创建`ValueStateDescriptor`,`ListStateDescriptor`,
-`ReducingStateDescriptor`,`FoldingStateDescriptor` 或 `MapStateDescriptor`。
+`ReducingStateDescriptor` 或 `MapStateDescriptor`。
-状态通过 `RuntimeContext` 进行访问,因此只能在 *rich functions* 中使用。请参阅[这里]({{site.baseurl}}/zh/dev/api_concepts.html#rich-functions)获取相关信息,
+状态通过 `RuntimeContext` 进行访问,因此只能在 *rich functions* 中使用。请参阅[这里]({% link dev/user_defined_functions.zh.md %}#rich-functions)获取相关信息,
但是我们很快也会看到一个例子。`RichFunction` 中 `RuntimeContext` 提供如下方法:
* `ValueState getState(ValueStateDescriptor)`
* `ReducingState getReducingState(ReducingStateDescriptor)`
* `ListState getListState(ListStateDescriptor)`
* `AggregatingState getAggregatingState(AggregatingStateDescriptor)`
-* `FoldingState getFoldingState(FoldingStateDescriptor)`
* `MapState getMapState(MapStateDescriptor)`
下面是一个 `FlatMapFunction` 的例子,展示了如何将这些部分组合起来:
@@ -219,7 +236,7 @@ object ExampleCountWindowAverage extends App {
.print()
// the printed output will be (1,4) and (1,5)
- env.execute("ExampleManagedState")
+ env.execute("ExampleKeyedState")
}
{% endhighlight %}
@@ -470,9 +487,44 @@ val counts: DataStream[(String, Int)] = stream
})
{% endhighlight %}
-## 使用 Managed Operator State
+## Operator State
+
+*Operator State* (or *non-keyed state*) is state that is is bound to one
+parallel operator instance. The [Kafka Connector]({% link
+dev/connectors/kafka.zh.md %}) is a good motivating example for the use of
+Operator State in Flink. Each parallel instance of the Kafka consumer maintains
+a map of topic partitions and offsets as its Operator State.
+
+The Operator State interfaces support redistributing state among parallel
+operator instances when the parallelism is changed. There are different schemes
+for doing this redistribution.
+
+In a typical stateful Flink Application you don't need operators state. It is
+mostly a special type of state that is used in source/sink implementations and
+scenarios where you don't have a key by which state can be partitioned.
+
+## Broadcast State
+
+*Broadcast State* is a special type of *Operator State*. It was introduced to
+support use cases where records of one stream need to be broadcasted to all
+downstream tasks, where they are used to maintain the same state among all
+subtasks. This state can then be accessed while processing records of a second
+stream. As an example where broadcast state can emerge as a natural fit, one
+can imagine a low-throughput stream containing a set of rules which we want to
+evaluate against all elements coming from another stream. Having the above type
+of use cases in mind, broadcast state differs from the rest of operator states
+in that:
+
+ 1. it has a map format,
+ 2. it is only available to specific operators that have as inputs a
+ *broadcasted* stream and a *non-broadcasted* one, and
+ 3. such an operator can have *multiple broadcast states* with different names.
+
+{% top %}
+
+## 使用 Operator State
-用户可以通过实现 `CheckpointedFunction` 或 `ListCheckpointed` 接口来使用 managed operator state。
+用户可以通过实现 `CheckpointedFunction` 接口来使用 operator state。
#### CheckpointedFunction
@@ -487,13 +539,14 @@ void initializeState(FunctionInitializationContext context) throws Exception;
进行 checkpoint 时会调用 `snapshotState()`。 用户自定义函数初始化时会调用 `initializeState()`,初始化包括第一次自定义函数初始化和从之前的 checkpoint 恢复。
因此 `initializeState()` 不仅是定义不同状态类型初始化的地方,也需要包括状态恢复的逻辑。
-当前,managed operator state 以 list 的形式存在。这些状态是一个 *可序列化* 对象的集合 `List`,彼此独立,方便在改变并发后进行状态的重新分派。
+当前 operator state 以 list 的形式存在。这些状态是一个 *可序列化* 对象的集合 `List`,彼此独立,方便在改变并发后进行状态的重新分派。
换句话说,这些对象是重新分配 non-keyed state 的最细粒度。根据状态的不同访问方式,有如下几种重新分配的模式:
- **Even-split redistribution:** 每个算子都保存一个列表形式的状态集合,整个状态由所有的列表拼接而成。当作业恢复或重新分配的时候,整个状态会按照算子的并发度进行均匀分配。
比如说,算子 A 的并发读为 1,包含两个元素 `element1` 和 `element2`,当并发读增加为 2 时,`element1` 会被分到并发 0 上,`element2` 则会被分到并发 1 上。
- **Union redistribution:** 每个算子保存一个列表形式的状态集合。整个状态由所有的列表拼接而成。当作业恢复或重新分配时,每个算子都将获得所有的状态数据。
+ Do not use this feature if your list may have high cardinality. Checkpoint metadata will store an offset to each list entry, which could lead to RPC framesize or out-of-memory errors.
下面的例子中的 `SinkFunction` 在 `CheckpointedFunction` 中进行数据缓存,然后统一发送到下游,这个例子演示了列表状态数据的 event-split redistribution。
diff --git a/docs/dev/table/catalogs.md b/docs/dev/table/catalogs.md
index 961d0a68af791f..4ff391791b5f09 100644
--- a/docs/dev/table/catalogs.md
+++ b/docs/dev/table/catalogs.md
@@ -43,94 +43,8 @@ The `GenericInMemoryCatalog` is an in-memory implementation of a catalog. All ob
### JdbcCatalog
-The `JdbcCatalog` enables users to connect Flink to relational databases over JDBC protocol.
-
-#### PostgresCatalog
-
-`PostgresCatalog` is the only implementation of JDBC Catalog at the moment.
-
-#### Usage of JdbcCatalog
-
-Set a `JdbcCatalog` with the following parameters:
-
-- name: required, name of the catalog
-- default database: required, default database to connect to
-- username: required, username of Postgres account
-- password: required, password of the account
-- base url: required, should be of format "jdbc:postgresql://:", and should not contain database name here
-
-
-
-{% highlight java %}
-
-EnvironmentSettings settings = EnvironmentSettings.newInstance().useBlinkPlanner().inStreamingMode().build();
-TableEnvironment tableEnv = TableEnvironment.create(settings);
-
-String name = "mypg";
-String defaultDatabase = "mydb";
-String username = "...";
-String password = "...";
-String baseUrl = "..."
-
-JdbcCatalog catalog = new JdbcCatalog(name, defaultDatabase, username, password, baseUrl);
-tableEnv.registerCatalog("mypg", catalog);
-
-// set the JdbcCatalog as the current catalog of the session
-tableEnv.useCatalog("mypg");
-{% endhighlight %}
-
-
-{% highlight scala %}
-
-val settings = EnvironmentSettings.newInstance().useBlinkPlanner().inStreamingMode().build()
-val tableEnv = TableEnvironment.create(settings)
-
-val name = "mypg"
-val defaultDatabase = "mydb"
-val username = "..."
-val password = "..."
-val baseUrl = "..."
-
-val catalog = new JdbcCatalog(name, defaultDatabase, username, password, baseUrl)
-tableEnv.registerCatalog("mypg", catalog)
-
-// set the JdbcCatalog as the current catalog of the session
-tableEnv.useCatalog("mypg")
-{% endhighlight %}
-
-{% highlight yaml %}
-
-execution:
- planner: blink
- ...
- current-catalog: mypg # set the JdbcCatalog as the current catalog of the session
- current-database: mydb
-
-catalogs:
- - name: mypg
- type: jdbc
- default-database: mydb
- username: ...
- password: ...
- base-url: ...
-{% endhighlight %}
-
-
-
+The `JdbcCatalog` enables users to connect Flink to relational databases over JDBC protocol. `PostgresCatalog` is the only implementation of JDBC Catalog at the moment.
+See [JdbcCatalog documentation]({% link dev/table/connectors/jdbc.md %}) for more details on setting up the catalog.
### HiveCatalog
@@ -154,8 +68,6 @@ The set of properties will be passed to a discovery service where the service tr
Users can use SQL DDL to create tables in catalogs in both Table API and SQL.
-For Table API:
-
+{% highlight scala %}
+val tableEnv = ...
-For SQL Client:
+// Create a HiveCatalog
+val catalog = new HiveCatalog("myhive", null, "", "")
+
+// Register the catalog
+tableEnv.registerCatalog("myhive", catalog)
+
+// Create a catalog database
+tableEnv.executeSql("CREATE DATABASE mydb WITH (...)")
+// Create a catalog table
+tableEnv.executeSql("CREATE TABLE mytable (name STRING, age INT) WITH (...)")
+
+tableEnv.listTables() // should return the tables in current catalog and database.
+
+{% endhighlight %}
+
+
{% highlight sql %}
// the catalog should have been registered via yaml file
Flink SQL> CREATE DATABASE mydb WITH (...);
@@ -190,17 +119,25 @@ Flink SQL> CREATE TABLE mytable (name STRING, age INT) WITH (...);
Flink SQL> SHOW TABLES;
mytable
{% endhighlight %}
+
+
+
For detailed information, please check out [Flink SQL CREATE DDL]({{ site.baseurl }}/dev/table/sql/create.html).
-### Using Java/Scala/Python API
+### Using Java/Scala
-Users can use Java, Scala, or Python API to create catalog tables programmatically.
+Users can use Java or Scala to create catalog tables programmatically.
{% highlight java %}
// create function
catalog.createFunction(new ObjectPath("mydb", "myfunc"), new CatalogFunctionImpl(...), false);
diff --git a/docs/dev/table/catalogs.zh.md b/docs/dev/table/catalogs.zh.md
index 23e0969672c8ac..5bd8e5630d7c73 100644
--- a/docs/dev/table/catalogs.zh.md
+++ b/docs/dev/table/catalogs.zh.md
@@ -39,94 +39,8 @@ Catalog 提供了元数据信息,例如数据库、表、分区、视图以及
### JdbcCatalog
-The `JdbcCatalog` enables users to connect Flink to relational databases over JDBC protocol.
-
-#### PostgresCatalog
-
-`PostgresCatalog` is the only implementation of JDBC Catalog at the moment.
-
-#### Usage of JdbcCatalog
-
-Set a `Jdbcatalog` with the following parameters:
-
-- name: required, name of the catalog
-- default database: required, default database to connect to
-- username: required, username of Postgres account
-- password: required, password of the account
-- base url: required, should be of format "jdbc:postgresql://:", and should not contain database name here
-
-
-
-{% highlight java %}
-
-EnvironmentSettings settings = EnvironmentSettings.newInstance().useBlinkPlanner().inStreamingMode().build();
-TableEnvironment tableEnv = TableEnvironment.create(settings);
-
-String name = "mypg";
-String defaultDatabase = "mydb";
-String username = "...";
-String password = "...";
-String baseUrl = "..."
-
-JdbcCatalog catalog = new JdbcCatalog(name, defaultDatabase, username, password, baseUrl);
-tableEnv.registerCatalog("mypg", catalog);
-
-// set the JdbcCatalog as the current catalog of the session
-tableEnv.useCatalog("mypg");
-{% endhighlight %}
-
-
-{% highlight scala %}
-
-val settings = EnvironmentSettings.newInstance().useBlinkPlanner().inStreamingMode().build()
-val tableEnv = TableEnvironment.create(settings)
-
-val name = "mypg"
-val defaultDatabase = "mydb"
-val username = "..."
-val password = "..."
-val baseUrl = "..."
-
-val catalog = new JdbcCatalog(name, defaultDatabase, username, password, baseUrl)
-tableEnv.registerCatalog("mypg", catalog)
-
-// set the JdbcCatalog as the current catalog of the session
-tableEnv.useCatalog("mypg")
-{% endhighlight %}
-
+{% highlight scala %}
+val tableEnv = ...
+
+// Create a HiveCatalog
+val catalog = new HiveCatalog("myhive", null, "", "");
-使用 SQL Client:
+// Register the catalog
+tableEnv.registerCatalog("myhive", catalog);
+// Create a catalog database
+tableEnv.executeSql("CREATE DATABASE mydb WITH (...)");
+
+// Create a catalog table
+tableEnv.executeSql("CREATE TABLE mytable (name STRING, age INT) WITH (...)");
+
+tableEnv.listTables(); // should return the tables in current catalog and database.
+
+{% endhighlight %}
+
+
{% highlight sql %}
// the catalog should have been registered via yaml file
Flink SQL> CREATE DATABASE mydb WITH (...);
@@ -186,17 +115,25 @@ Flink SQL> CREATE TABLE mytable (name STRING, age INT) WITH (...);
Flink SQL> SHOW TABLES;
mytable
{% endhighlight %}
+
{% highlight java %}
// create function
catalog.createFunction(new ObjectPath("mydb", "myfunc"), new CatalogFunctionImpl(...), false);
diff --git a/docs/dev/table/common.md b/docs/dev/table/common.md
index 907cb15ac1013f..e8a1861549b808 100644
--- a/docs/dev/table/common.md
+++ b/docs/dev/table/common.md
@@ -32,12 +32,11 @@ Main Differences Between the Two Planners
1. Blink treats batch jobs as a special case of streaming. As such, the conversion between Table and DataSet is also not supported, and batch jobs will not be translated into `DateSet` programs but translated into `DataStream` programs, the same as the streaming jobs.
2. The Blink planner does not support `BatchTableSource`, use bounded `StreamTableSource` instead of it.
-3. The Blink planner only support the brand new `Catalog` and does not support `ExternalCatalog` which is deprecated.
-4. The implementations of `FilterableTableSource` for the old planner and the Blink planner are incompatible. The old planner will push down `PlannerExpression`s into `FilterableTableSource`, while the Blink planner will push down `Expression`s.
-5. String based key-value config options (Please see the documentation about [Configuration]({{ site.baseurl }}/dev/table/config.html) for details) are only used for the Blink planner.
-6. The implementation(`CalciteConfig`) of `PlannerConfig` in two planners is different.
-7. The Blink planner will optimize multiple-sinks into one DAG (supported only on `TableEnvironment`, not on `StreamTableEnvironment`). The old planner will always optimize each sink into a new DAG, where all DAGs are independent of each other.
-8. The old planner does not support catalog statistics now, while the Blink planner does.
+3. The implementations of `FilterableTableSource` for the old planner and the Blink planner are incompatible. The old planner will push down `PlannerExpression`s into `FilterableTableSource`, while the Blink planner will push down `Expression`s.
+4. String based key-value config options (Please see the documentation about [Configuration]({{ site.baseurl }}/dev/table/config.html) for details) are only used for the Blink planner.
+5. The implementation(`CalciteConfig`) of `PlannerConfig` in two planners is different.
+6. The Blink planner will optimize multiple-sinks into one DAG on both `TableEnvironment` and `StreamTableEnvironment`. The old planner will always optimize each sink into a new DAG, where all DAGs are independent of each other.
+7. The old planner does not support catalog statistics now, while the Blink planner does.
Structure of Table API and SQL Programs
@@ -63,10 +62,8 @@ Table tapiResult = tableEnv.from("table1").select(...);
Table sqlResult = tableEnv.sqlQuery("SELECT ... FROM table1 ... ");
// emit a Table API result Table to a TableSink, same for SQL result
-tapiResult.insertInto("outputTable");
-
-// execute
-tableEnv.execute("java_job");
+TableResult tableResult = tapiResult.executeInsert("outputTable");
+tableResult...
{% endhighlight %}
@@ -88,10 +85,8 @@ val tapiResult = tableEnv.from("table1").select(...)
val sqlResult = tableEnv.sqlQuery("SELECT ... FROM table1 ...")
// emit a Table API result Table to a TableSink, same for SQL result
-tapiResult.insertInto("outputTable")
-
-// execute
-tableEnv.execute("scala_job")
+val tableResult = tapiResult.executeInsert("outputTable")
+tableResult...
{% endhighlight %}
@@ -114,10 +109,8 @@ tapi_result = table_env.from_path("table1").select(...)
sql_result = table_env.sql_query("SELECT ... FROM table1 ...")
# emit a Table API result Table to a TableSink, same for SQL result
-tapi_result.insert_into("outputTable")
-
-# execute
-table_env.execute("python_job")
+table_result = tapi_result.execute_insert("outputTable")
+table_result...
{% endhighlight %}
@@ -552,7 +545,9 @@ val revenue = orders
// execute query
{% endhighlight %}
-**Note:** The Scala Table API uses Scala Symbols, which start with a single tick (`'`) to reference the attributes of a `Table`. The Table API uses Scala implicits. Make sure to import `org.apache.flink.api.scala._` and `org.apache.flink.table.api.scala._` in order to use Scala implicit conversions.
+**Note:** The Scala Table API uses Scala String interpolation that starts with a dollar sign (`$`) to reference the attributes of a `Table`. The Table API uses Scala implicits. Make sure to import
+* `org.apache.flink.table.api._` - for implicit expression conversions
+* `org.apache.flink.api.scala._` and `org.apache.flink.table.api.bridge.scala._` if you want to convert from/to DataStream.
@@ -661,7 +656,7 @@ TableEnvironment tableEnv = ...; // see "Create a TableEnvironment" section
// register "RevenueFrance" output table
// compute revenue for all customers from France and emit to "RevenueFrance"
-tableEnv.sqlUpdate(
+tableEnv.executeSql(
"INSERT INTO RevenueFrance " +
"SELECT cID, cName, SUM(revenue) AS revSum " +
"FROM Orders " +
@@ -669,7 +664,6 @@ tableEnv.sqlUpdate(
"GROUP BY cID, cName"
);
-// execute query
{% endhighlight %}
@@ -682,7 +676,7 @@ val tableEnv = ... // see "Create a TableEnvironment" section
// register "RevenueFrance" output table
// compute revenue for all customers from France and emit to "RevenueFrance"
-tableEnv.sqlUpdate("""
+tableEnv.executeSql("""
|INSERT INTO RevenueFrance
|SELECT cID, cName, SUM(revenue) AS revSum
|FROM Orders
@@ -690,7 +684,6 @@ tableEnv.sqlUpdate("""
|GROUP BY cID, cName
""".stripMargin)
-// execute query
{% endhighlight %}
@@ -704,7 +697,7 @@ table_env = ... # see "Create a TableEnvironment" section
# register "RevenueFrance" output table
# compute revenue for all customers from France and emit to "RevenueFrance"
-table_env.sql_update(
+table_env.execute_sql(
"INSERT INTO RevenueFrance "
"SELECT cID, cName, SUM(revenue) AS revSum "
"FROM Orders "
@@ -712,7 +705,6 @@ table_env.sql_update(
"GROUP BY cID, cName"
)
-# execute query
{% endhighlight %}
@@ -737,7 +729,7 @@ A batch `Table` can only be written to a `BatchTableSink`, while a streaming `Ta
Please see the documentation about [Table Sources & Sinks]({{ site.baseurl }}/dev/table/sourceSinks.html) for details about available sinks and instructions for how to implement a custom `TableSink`.
-The `Table.insertInto(String tableName)` method emits the `Table` to a registered `TableSink`. The method looks up the `TableSink` from the catalog by the name and validates that the schema of the `Table` is identical to the schema of the `TableSink`.
+The `Table.executeInsert(String tableName)` method emits the `Table` to a registered `TableSink`. The method looks up the `TableSink` from the catalog by the name and validates that the schema of the `Table` is identical to the schema of the `TableSink`.
The following examples shows how to emit a `Table`:
@@ -760,10 +752,10 @@ tableEnv.connect(new FileSystem("/path/to/file"))
// compute a result Table using Table API operators and/or SQL queries
Table result = ...
+
// emit the result Table to the registered TableSink
-result.insertInto("CsvSinkTable");
+result.executeInsert("CsvSinkTable");
-// execute the program
{% endhighlight %}
@@ -787,9 +779,8 @@ tableEnv.connect(new FileSystem("/path/to/file"))
val result: Table = ...
// emit the result Table to the registered TableSink
-result.insertInto("CsvSinkTable")
+result.executeInsert("CsvSinkTable")
-// execute the program
{% endhighlight %}
@@ -799,7 +790,7 @@ result.insertInto("CsvSinkTable")
table_env = ... # see "Create a TableEnvironment" section
# create a TableSink
-t_env.connect(FileSystem().path("/path/to/file")))
+table_env.connect(FileSystem().path("/path/to/file")))
.with_format(Csv()
.field_delimiter(',')
.deriveSchema())
@@ -813,9 +804,8 @@ t_env.connect(FileSystem().path("/path/to/file")))
result = ...
# emit the result Table to the registered TableSink
-result.insert_into("CsvSinkTable")
+result.execute_insert("CsvSinkTable")
-# execute the program
{% endhighlight %}
@@ -829,40 +819,43 @@ Translate and Execute a Query
The behavior of translating and executing a query is different for the two planners.
-
-Table API and SQL queries are translated into [DataStream]({{ site.baseurl }}/dev/datastream_api.html) or [DataSet]({{ site.baseurl }}/dev/batch) programs depending on whether their input is a streaming or batch input. A query is internally represented as a logical query plan and is translated in two phases:
-
-1. Optimization of the logical plan
-2. Translation into a DataStream or DataSet program
-For streaming, a Table API or SQL query is translated when:
+
+Table API and SQL queries are translated into [DataStream]({{ site.baseurl }}/dev/datastream_api.html) programs whether their input is streaming or batch. A query is internally represented as a logical query plan and is translated in two phases:
-* `TableEnvironment.execute()` is called. A `Table` (emitted to a `TableSink` through `Table.insertInto()`) or a SQL update query (specified through `TableEnvironment.sqlUpdate()`) will be buffered in `TableEnvironment` first. Each sink will be optimized independently. The execution graph contains multiple independent sub-DAGs.
-* A `Table` is translated when it is converted into a `DataStream` (see [Integration with DataStream and DataSet API](#integration-with-datastream-and-dataset-api)). Once translated, it's a regular DataStream program and is executed when `StreamExecutionEnvironment.execute()` is called.
+1. Optimization of the logical plan,
+2. Translation into a DataStream program.
-For batch, a Table API or SQL query is translated when:
+a Table API or SQL query is translated when:
-* a `Table` is emitted to a `TableSink`, i.e., when `Table.insertInto()` is called.
-* a SQL update query is specified, i.e., when `TableEnvironment.sqlUpdate()` is called.
-* a `Table` is converted into a `DataSet` (see [Integration with DataStream and DataSet API](#integration-with-datastream-and-dataset-api)).
+* `TableEnvironment.executeSql()` is called. This method is used for executing a given statement, and the sql query is translated immediately once this method is called.
+* `Table.executeInsert()` is called. This method is used for inserting the table content to the given sink path, and the Table API is translated immediately once this method is called.
+* `Table.execute()` is called. This method is used for collecting the table content to local client, and the Table API is translated immediately once this method is called.
+* `StatementSet.execute()` is called. A `Table` (emitted to a sink through `StatementSet.addInsert()`) or an INSERT statement (specified through `StatementSet.addInsertSql()`) will be buffered in `StatementSet` first. They are translated once `StatementSet.execute()` is called. All sinks will be optimized into one DAG.
+* A `Table` is translated when it is converted into a `DataStream` (see [Integration with DataStream and DataSet API](#integration-with-datastream-and-dataset-api)). Once translated, it's a regular DataStream program and is executed when `StreamExecutionEnvironment.execute()` is called.
-Once translated, a Table API or SQL query is handled like a regular DataSet program and is executed when `ExecutionEnvironment.execute()` is called.
+Attention **Since 1.11 version, `sqlUpdate()` method and `insertInto()` method are deprecated. If the Table program is built from these two methods, we must use `StreamTableEnvironment.execute()` method instead of `StreamExecutionEnvironment.execute()` method to execute it.**
-
-Table API and SQL queries are translated into [DataStream]({{ site.baseurl }}/dev/datastream_api.html) programs whether their input is streaming or batch. A query is internally represented as a logical query plan and is translated in two phases:
+
+Table API and SQL queries are translated into [DataStream]({{ site.baseurl }}/dev/datastream_api.html) or [DataSet]({{ site.baseurl }}/dev/batch) programs depending on whether their input is a streaming or batch input. A query is internally represented as a logical query plan and is translated in two phases:
-1. Optimization of the logical plan,
-2. Translation into a DataStream program.
+1. Optimization of the logical plan
+2. Translation into a DataStream or DataSet program
-a Table API or SQL query is translated when:
+A Table API or SQL query is translated when:
-* `TableEnvironment.execute()` is called. A `Table` (emitted to a `TableSink` through `Table.insertInto()`) or a SQL update query (specified through `TableEnvironment.sqlUpdate()`) will be buffered in `TableEnvironment` first. All sinks will be optimized into one DAG.
-* A `Table` is translated when it is converted into a `DataStream` (see [Integration with DataStream and DataSet API](#integration-with-datastream-and-dataset-api)). Once translated, it's a regular DataStream program and is executed when `StreamExecutionEnvironment.execute()` is called.
+* `TableEnvironment.executeSql()` is called. This method is used for executing a given statement, and the sql query is translated immediately once this method is called.
+* `Table.executeInsert()` is called. This method is used for inserting the table content to the given sink path, and the Table API is translated immediately once this method is called.
+* `Table.execute()` is called. This method is used for collecting the table content to local client, and the Table API is translated immediately once this method is called.
+* `StatementSet.execute()` is called. A `Table` (emitted to a sink through `StatementSet.addInsert()`) or an INSERT statement (specified through `StatementSet.addInsertSql()`) will be buffered in `StatementSet` first. They are translated once `StatementSet.execute()` is called. Each sink will be optimized independently. The execution graph contains multiple independent sub-DAGs.
+* For streaming, a `Table` is translated when it is converted into a `DataStream` (see [Integration with DataStream and DataSet API](#integration-with-datastream-and-dataset-api)). Once translated, it's a regular DataStream program and is executed when `StreamExecutionEnvironment.execute()` is called. For batch, a `Table` is translated when it is converted into a `DataSet` (see [Integration with DataStream and DataSet API](#integration-with-datastream-and-dataset-api)). Once translated, it's a regular DataSet program and is executed when `ExecutionEnvironment.execute()` is called.
+Attention **Since 1.11 version, `sqlUpdate()` method and `insertInto()` method are deprecated. For streaming, if the Table program is built from these two methods, we must use `StreamTableEnvironment.execute()` method instead of `StreamExecutionEnvironment.execute()` method to execute it. For batch, if the Table program is built from these two methods, we must use `BatchTableEnvironment.execute()` method instead of `ExecutionEnvironment.execute()` method to execute it.**
+
{% top %}
@@ -879,7 +872,7 @@ This interaction can be achieved by converting a `DataStream` or `DataSet` into
### Implicit Conversion for Scala
-The Scala Table API features implicit conversions for the `DataSet`, `DataStream`, and `Table` classes. These conversions are enabled by importing the package `org.apache.flink.table.api.scala._` in addition to `org.apache.flink.api.scala._` for the Scala DataStream API.
+The Scala Table API features implicit conversions for the `DataSet`, `DataStream`, and `Table` classes. These conversions are enabled by importing the package `org.apache.flink.table.api.bridge.scala._` in addition to `org.apache.flink.api.scala._` for the Scala DataStream API.
### Create a View from a DataStream or DataSet
@@ -1039,7 +1032,9 @@ val retractStream: DataStream[(Boolean, Row)] = tableEnv.toRetractStream[Row](ta
-**Note:** A detailed discussion about dynamic tables and their properties is given in the [Dynamic Tables](streaming/dynamic_tables.html) document.
+**Note:** A detailed discussion about dynamic tables and their properties is given in the [Dynamic Tables](streaming/dynamic_tables.html) document.
+
+Attention **Once the Table is converted to a DataStream, please use the `StreamExecutionEnvironment.execute()` method to execute the DataStream program.**
#### Convert a Table into a DataSet
@@ -1084,6 +1079,8 @@ val dsTuple: DataSet[(String, Int)] = tableEnv.toDataSet[(String, Int)](table)
+Attention **Once the Table is converted to a DataSet, we must use the ExecutionEnvironment.execute method to execute the DataSet program.**
+
{% top %}
### Mapping of Data Types to Table Schema
@@ -1405,16 +1402,7 @@ Query Optimization
------------------
-
-
-Apache Flink leverages Apache Calcite to optimize and translate queries. The optimization currently performed include projection and filter push-down, subquery decorrelation, and other kinds of query rewriting. Old planner does not yet optimize the order of joins, but executes them in the same order as defined in the query (order of Tables in the `FROM` clause and/or order of join predicates in the `WHERE` clause).
-
-It is possible to tweak the set of optimization rules which are applied in different phases by providing a `CalciteConfig` object. This can be created via a builder by calling `CalciteConfig.createBuilder())` and is provided to the TableEnvironment by calling `tableEnv.getConfig.setPlannerConfig(calciteConfig)`.
-
-
-
-
Apache Flink leverages and extends Apache Calcite to perform sophisticated query optimization.
This includes a series of rule and cost-based optimizations such as:
@@ -1434,21 +1422,29 @@ This includes a series of rule and cost-based optimizations such as:
The optimizer makes intelligent decisions, based not only on the plan but also rich statistics available from the data sources and fine-grain costs for each operator such as io, cpu, network, and memory.
Advanced users may provide custom optimizations via a `CalciteConfig` object that can be provided to the table environment by calling `TableEnvironment#getConfig#setPlannerConfig`.
+
+
+Apache Flink leverages Apache Calcite to optimize and translate queries. The optimization currently performed include projection and filter push-down, subquery decorrelation, and other kinds of query rewriting. Old planner does not yet optimize the order of joins, but executes them in the same order as defined in the query (order of Tables in the `FROM` clause and/or order of join predicates in the `WHERE` clause).
+
+It is possible to tweak the set of optimization rules which are applied in different phases by providing a `CalciteConfig` object. This can be created via a builder by calling `CalciteConfig.createBuilder())` and is provided to the TableEnvironment by calling `tableEnv.getConfig.setPlannerConfig(calciteConfig)`.
-### Explaining a Table
+Explaining a Table
+------------------
The Table API provides a mechanism to explain the logical and optimized query plans to compute a `Table`.
-This is done through the `TableEnvironment.explain(table)` method or `TableEnvironment.explain()` method. `explain(table)` returns the plan of a given `Table`. `explain()` returns the result of a multiple sinks plan and is mainly used for the Blink planner. It returns a String describing three plans:
+This is done through the `Table.explain()` method or `StatementSet.explain()` method. `Table.explain()`returns the plan of a `Table`. `StatementSet.explain()` returns the plan of multiple sinks. It returns a String describing three plans:
1. the Abstract Syntax Tree of the relational query, i.e., the unoptimized logical query plan,
2. the optimized logical query plan, and
3. the physical execution plan.
-The following code shows an example and the corresponding output for given `Table` using `explain(table)`:
+`TableEnvironment.explainSql()` and `TableEnvironment.executeSql()` support execute a `EXPLAIN` statement to get the plans, Please refer to [EXPLAIN]({{ site.baseurl }}/dev/table/sql/explain.html) page.
+
+The following code shows an example and the corresponding output for given `Table` using `Table.explain()` method:
-The following code shows an example and the corresponding output for multiple-sinks plan using `explain()`:
+The following code shows an example and the corresponding output for multiple-sinks plan using `StatementSet.explain()` method:
@@ -531,7 +533,9 @@ val revenue = orders
// execute query
{% endhighlight %}
-**Note:** The Scala Table API uses Scala Symbols, which start with a single tick (`'`) to reference the attributes of a `Table`. The Table API uses Scala implicits. Make sure to import `org.apache.flink.api.scala._` and `org.apache.flink.table.api.scala._` in order to use Scala implicit conversions.
+**Note:** The Scala Table API uses Scala String interpolation that starts with a dollar sign (`$`) to reference the attributes of a `Table`. The Table API uses Scala implicits. Make sure to import
+* `org.apache.flink.table.api._` - for implicit expression conversions
+* `org.apache.flink.api.scala._` and `org.apache.flink.table.api.bridge.scala._` if you want to convert from/to DataStream.
@@ -561,7 +565,7 @@ revenue = orders \
Flink SQL 是基于实现了SQL标准的 [Apache Calcite](https://calcite.apache.org) 的。SQL 查询由常规字符串指定。
-文档 [SQL]({{ site.baseurl }}/zh/dev/table/sql.html) 描述了Flink对流处理和批处理表的SQL支持。
+文档 [SQL]({{ site.baseurl }}/zh/dev/table/sql/index.html) 描述了Flink对流处理和批处理表的SQL支持。
下面的示例演示了如何指定查询并将结果作为 `Table` 对象返回。
@@ -640,7 +644,7 @@ TableEnvironment tableEnv = ...; // see "Create a TableEnvironment" section
// register "RevenueFrance" output table
// compute revenue for all customers from France and emit to "RevenueFrance"
-tableEnv.sqlUpdate(
+tableEnv.executeSql(
"INSERT INTO RevenueFrance " +
"SELECT cID, cName, SUM(revenue) AS revSum " +
"FROM Orders " +
@@ -648,7 +652,6 @@ tableEnv.sqlUpdate(
"GROUP BY cID, cName"
);
-// execute query
{% endhighlight %}
@@ -661,7 +664,7 @@ val tableEnv = ... // see "Create a TableEnvironment" section
// register "RevenueFrance" output table
// compute revenue for all customers from France and emit to "RevenueFrance"
-tableEnv.sqlUpdate("""
+tableEnv.executeSql("""
|INSERT INTO RevenueFrance
|SELECT cID, cName, SUM(revenue) AS revSum
|FROM Orders
@@ -669,7 +672,6 @@ tableEnv.sqlUpdate("""
|GROUP BY cID, cName
""".stripMargin)
-// execute query
{% endhighlight %}
@@ -683,7 +685,7 @@ table_env = ... # see "Create a TableEnvironment" section
# register "RevenueFrance" output table
# compute revenue for all customers from France and emit to "RevenueFrance"
-table_env.sql_update(
+table_env.execute_sql(
"INSERT INTO RevenueFrance "
"SELECT cID, cName, SUM(revenue) AS revSum "
"FROM Orders "
@@ -691,7 +693,6 @@ table_env.sql_update(
"GROUP BY cID, cName"
)
-# execute query
{% endhighlight %}
@@ -716,7 +717,7 @@ Table API 和 SQL 查询的混用非常简单因为它们都返回 `Table` 对
请参考文档 [Table Sources & Sinks]({{ site.baseurl }}/zh/dev/table/sourceSinks.html) 以获取更多关于可用 Sink 的信息以及如何自定义 `TableSink`。
-方法 `Table.insertInto(String tableName)` 将 `Table` 发送至已注册的 `TableSink`。该方法通过名称在 catalog 中查找 `TableSink` 并确认`Table` schema 和 `TableSink` schema 一致。
+方法 `Table.executeInsert(String tableName)` 将 `Table` 发送至已注册的 `TableSink`。该方法通过名称在 catalog 中查找 `TableSink` 并确认`Table` schema 和 `TableSink` schema 一致。
下面的示例演示如何输出 `Table`:
@@ -740,9 +741,8 @@ tableEnv.connect(new FileSystem("/path/to/file"))
// compute a result Table using Table API operators and/or SQL queries
Table result = ...
// emit the result Table to the registered TableSink
-result.insertInto("CsvSinkTable");
+result.executeInsert("CsvSinkTable");
-// execute the program
{% endhighlight %}
@@ -766,9 +766,8 @@ tableEnv.connect(new FileSystem("/path/to/file"))
val result: Table = ...
// emit the result Table to the registered TableSink
-result.insertInto("CsvSinkTable")
+result.executeInsert("CsvSinkTable")
-// execute the program
{% endhighlight %}
@@ -778,7 +777,7 @@ result.insertInto("CsvSinkTable")
table_env = ... # see "Create a TableEnvironment" section
# create a TableSink
-t_env.connect(FileSystem().path("/path/to/file")))
+table_env.connect(FileSystem().path("/path/to/file")))
.with_format(Csv()
.field_delimiter(',')
.deriveSchema())
@@ -792,9 +791,8 @@ t_env.connect(FileSystem().path("/path/to/file")))
result = ...
# emit the result Table to the registered TableSink
-result.insert_into("CsvSinkTable")
+result.execute_insert("CsvSinkTable")
-# execute the program
{% endhighlight %}
diff --git a/docs/dev/table/config.md b/docs/dev/table/config.md
index dd312d6fc558ed..68a73bacf2eaea 100644
--- a/docs/dev/table/config.md
+++ b/docs/dev/table/config.md
@@ -104,3 +104,9 @@ The following options can be used to tune the performance of the query execution
The following options can be used to adjust the behavior of the query optimizer to get a better execution plan.
{% include generated/optimizer_config_configuration.html %}
+
+### Table Options
+
+The following options can be used to adjust the behavior of the table planner.
+
+{% include generated/table_config_configuration.html %}
diff --git a/docs/dev/table/config.zh.md b/docs/dev/table/config.zh.md
index 9d325e6035d983..3c41bdc825a8f4 100644
--- a/docs/dev/table/config.zh.md
+++ b/docs/dev/table/config.zh.md
@@ -96,3 +96,9 @@ configuration.set_string("table.exec.mini-batch.size", "5000");
以下配置可以用于调整查询优化器的行为以获得更好的执行计划。
{% include generated/optimizer_config_configuration.html %}
+
+### Planner 配置
+
+以下配置可以用于调整 planner 的行为。
+
+{% include generated/table_config_configuration.html %}
diff --git a/docs/dev/table/connect.md b/docs/dev/table/connect.md
index 52a22b539b8de7..7aa151bf7de1e7 100644
--- a/docs/dev/table/connect.md
+++ b/docs/dev/table/connect.md
@@ -1,7 +1,5 @@
---
-title: "Table API Connectors"
-nav-parent_id: connectors-root
-nav-pos: 2
+title: "Table API Legacy Connectors"
---
+
+Sink
+Sink
+
+* This will be replaced by the TOC
+{:toc}
+
+The BlackHole connector allows for swallowing all input records. It is designed for:
+
+- high performance testing.
+- UDF to output, not substantive sink.
+
+Just like /dev/null device on Unix-like operating systems.
+
+The BlackHole connector is built-in.
+
+How to create a BlackHole table
+----------------
+
+
diff --git a/docs/dev/table/connectors/datagen.md b/docs/dev/table/connectors/datagen.md
new file mode 100644
index 00000000000000..ad1e6afe5432f1
--- /dev/null
+++ b/docs/dev/table/connectors/datagen.md
@@ -0,0 +1,152 @@
+---
+title: "DataGen SQL Connector"
+nav-title: DataGen
+nav-parent_id: sql-connectors
+nav-pos: 10
+---
+
+
+Scan Source: Bounded
+Scan Source: UnBounded
+
+* This will be replaced by the TOC
+{:toc}
+
+The DataGen connector allows for reading by data generation rules.
+
+The DataGen connector can work with [Computed Column syntax]({% link dev/table/sql/create.md %}#create-table).
+This allows you to generate records flexibly.
+
+The DataGen connector is built-in.
+
+Attention Complex types are not supported: Array, Map, Row. Please construct these types by computed column.
+
+How to create a DataGen table
+----------------
+
+The boundedness of table: when the generation of field data in the table is completed, the reading
+is finished. So the boundedness of the table depends on the boundedness of fields.
+
+For each field, there are two ways to generate data:
+
+- Random generator is the default generator, you can specify random max and min values. For char/varchar/string, the length can be specified. It is a unbounded generator.
+- Sequence generator, you can specify sequence start and end values. It is a bounded generator, when the sequence number reaches the end value, the reading ends.
+
+
diff --git a/docs/dev/table/connectors/elasticsearch.md b/docs/dev/table/connectors/elasticsearch.md
new file mode 100644
index 00000000000000..c4b2a98b7650b4
--- /dev/null
+++ b/docs/dev/table/connectors/elasticsearch.md
@@ -0,0 +1,268 @@
+---
+title: "Elasticsearch SQL Connector"
+nav-title: Elasticsearch
+nav-parent_id: sql-connectors
+nav-pos: 4
+---
+
+
+Sink: Batch
+Sink: Streaming Append & Upsert Mode
+
+* This will be replaced by the TOC
+{:toc}
+
+The Elasticsearch connector allows for writing into an index of the Elasticsearch engine. This document describes how to setup the Elasticsearch Connector to run SQL queries against Elasticsearch.
+
+The connector can operate in upsert mode for exchanging UPDATE/DELETE messages with the external system using the primary key defined on the DDL.
+
+If no primary key is defined on the DDL, the connector can only operate in append mode for exchanging INSERT only messages with external system.
+
+Dependencies
+------------
+
+In order to setup the Elasticsearch connector, the following table provides dependency information for both projects using a build automation tool (such as Maven or SBT) and SQL Client with SQL JAR bundles.
+
+| Elasticsearch Version | Maven dependency | SQL Client JAR |
+| :---------------------- | :----------------------------------------------------------------- | :----------------------|
+| 6.x | `flink-connector-elasticsearch6{{site.scala_version_suffix}}` | {% if site.is_stable %} [Download](https://repo.maven.apache.org/maven2/org/apache/flink/flink-sql-connector-elasticsearch6{{site.scala_version_suffix}}/{{site.version}}/flink-sql-connector-elasticsearch6{{site.scala_version_suffix}}-{{site.version}}.jar) {% else %} Only available for [stable releases]({{ site.stable_baseurl }}/dev/table/connectors/elasticsearch.html) {% endif %}|
+| 7.x and later versions | `flink-connector-elasticsearch7{{site.scala_version_suffix}}` | {% if site.is_stable %} [Download](https://repo.maven.apache.org/maven2/org/apache/flink/flink-sql-connector-elasticsearch7{{site.scala_version_suffix}}/{{site.version}}/flink-sql-connector-elasticsearch7{{site.scala_version_suffix}}-{{site.version}}.jar) {% else %} Only available for [stable releases]({{ site.stable_baseurl }}/dev/table/connectors/elasticsearch.html) {% endif %}|
+
+
+Attention Elasticsearch connector works with JSON format which defines how to encode documents for the external system, therefore, it must be added as a [dependency]({% link dev/table/connectors/formats/index.md %}).
+
+How to create an Elasticsearch table
+----------------
+
+The example below shows how to create an Elasticsearch sink table:
+
+
Specify what connector to use, valid values are:
+
+
elasticsearch-6: connect to Elasticsearch 6.x cluster
+
elasticsearch-7: connect to Elasticsearch 7.x and later versions cluster
+
+
+
+
hosts
+
required
+
(none)
+
String
+
One or more Elasticsearch hosts to connect to, e.g. 'http://host_name:9092;http://host_name:9093'.
+
+
+
index
+
required
+
(none)
+
String
+
Elasticsearch index for every record. Can be a static index (e.g. 'myIndex') or
+ a dynamic index (e.g. 'index-{log_ts|yyyy-MM-dd}').
+ See the following Dynamic Index section for more details.
+
+
+
document-type
+
required in 6.x
+
(none)
+
String
+
Elasticsearch document type. Not necessary anymore in elasticsearch-7.
+
+
+
document-id.key-delimiter
+
optional
+
_
+
String
+
Delimiter for composite keys ("_" by default), e.g., "$" would result in IDs "KEY1$KEY2$KEY3"."
+
+
+
failure-handler
+
optional
+
fail
+
String
+
Failure handling strategy in case a request to Elasticsearch fails. Valid strategies are:
+
+
fail: throws an exception if a request fails and thus causes a job failure.
+
ignore: ignores failures and drops the request.
+
retry_rejected: re-adds requests that have failed due to queue capacity saturation.
+
custom class name: for failure handling with a ActionRequestFailureHandler subclass.
+
+
+
+
+
sink.flush-on-checkpoint
+
optional
+
true
+
Boolean
+
Flush on checkpoint or not. When disabled, a sink will not wait for all pending action requests
+ to be acknowledged by Elasticsearch on checkpoints. Thus, a sink does NOT provide any strong
+ guarantees for at-least-once delivery of action requests.
+
+
+
+
sink.bulk-flush.max-actions
+
optional
+
1000
+
Integer
+
Maximum number of buffered actions per bulk request.
+ Can be set to '0' to disable it.
+
+
+
+
sink.bulk-flush.max-size
+
optional
+
2mb
+
MemorySize
+
Maximum size in memory of buffered actions per bulk request. Must be in MB granularity.
+ Can be set to '0' to disable it.
+
+
+
+
sink.bulk-flush.interval
+
optional
+
1s
+
Duration
+
The interval to flush buffered actions.
+ Can be set to '0' to disable it. Note, both 'sink.bulk-flush.max-size' and 'sink.bulk-flush.max-actions'
+ can be set to '0' with the flush interval set allowing for complete async processing of buffered actions.
+
+
+
+
sink.bulk-flush.backoff.strategy
+
optional
+
DISABLED
+
String
+
Specify how to perform retries if any flush actions failed due to a temporary request error. Valid strategies are:
+
+
DISABLED: no retry performed, i.e. fail after the first request error.
+
CONSTANT: wait for backoff delay between retries.
+
EXPONENTIAL: initially wait for backoff delay and increase exponentially between retries.
+
+
+
+
+
sink.bulk-flush.backoff.max-retries
+
optional
+
8
+
Integer
+
Maximum number of backoff retries.
+
+
+
sink.bulk-flush.backoff.delay
+
optional
+
50ms
+
Duration
+
Delay between each backoff attempt. For CONSTANT backoff, this is simply the delay between each retry. For EXPONENTIAL backoff, this is the initial base delay.
+
+
+
connection.max-retry-timeout
+
optional
+
(none)
+
Duration
+
Maximum timeout between retries.
+
+
+
connection.path-prefix
+
optional
+
(none)
+
String
+
Prefix string to be added to every REST communication, e.g., '/v1'
+
+
+
format
+
optional
+
json
+
String
+
Elasticsearch connector supports to specify a format. The format must produce a valid json document.
+ By default uses built-in 'json' format. Please refer to JSON Format page for more details.
+
+
+
+
+
+Features
+----------------
+
+### Key Handling
+
+Elasticsearch sink can work in either upsert mode or append mode, it depends on whether primary key is defined.
+If primary key is defined, Elasticsearch sink works in upsert mode which can consume queries containing UPDATE/DELETE messages.
+If primary key is not defined, Elasticsearch sink works in append mode which can only consume queries containing INSERT only messages.
+
+In Elasticsearch connector, the primary key is used to calculate the Elasticsearch document id, which is a string of up to 512 bytes. It cannot have whitespaces.
+The Elasticsearch connector generates a document ID string for every row by concatenating all primary key fields in the order defined in the DDL using a key delimiter specified by `document-id.key-delimiter`.
+Certain types are not allowed as primary key field as they do not have a good string representation, e.g. `BYTES`, `ROW`, `ARRAY`, `MAP`, etc.
+If no primary key is specified, Elasticsearch will generate a document id automatically.
+
+See [CREATE TABLE DDL]({% link dev/table/sql/create.md %}#create-table) for more details about PRIMARY KEY syntax.
+
+### Dynamic Index
+
+Elasticsearch sink supports both static index and dynamic index.
+
+If you want to have a static index, the `index` option value should be a plain string, e.g. `'myusers'`, all the records will be consistently written into "myusers" index.
+
+If you want to have a dynamic index, you can use `{field_name}` to reference a field value in the record to dynamically generate a target index.
+You can also use `'{field_name|date_format_string}'` to convert a field value of `TIMESTAMP/DATE/TIME` type into the format specified by the `date_format_string`.
+The `date_format_string` is compatible with Java's [DateTimeFormatter](https://docs.oracle.com/javase/8/docs/api/index.html).
+For example, if the option value is `'myusers-{log_ts|yyyy-MM-dd}'`, then a record with `log_ts` field value `2020-03-27 12:25:55` will be written into "myusers-2020-03-27" index.
+
+
+Data Type Mapping
+----------------
+
+Elasticsearch stores document in a JSON string. So the data type mapping is between Flink data type and JSON data type.
+Flink uses built-in `'json'` format for Elasticsearch connector. Please refer to [JSON Format]({% link dev/table/connectors/formats/json.md %}) page for more type mapping details.
+
+{% top %}
\ No newline at end of file
diff --git a/docs/dev/table/connectors/elasticsearch.zh.md b/docs/dev/table/connectors/elasticsearch.zh.md
new file mode 100644
index 00000000000000..21c69468966054
--- /dev/null
+++ b/docs/dev/table/connectors/elasticsearch.zh.md
@@ -0,0 +1,268 @@
+---
+title: "Elasticsearch SQL Connector"
+nav-title: Elasticsearch
+nav-parent_id: sql-connectors
+nav-pos: 4
+---
+
+
+Sink: Batch
+Sink: Streaming Append & Upsert Mode
+
+* This will be replaced by the TOC
+{:toc}
+
+The Elasticsearch connector allows for writing into an index of the Elasticsearch engine. This document describes how to setup the Elasticsearch Connector to run SQL queries against Elasticsearch.
+
+The connector can operate in upsert mode for exchanging UPDATE/DELETE messages with the external system using the primary key defined on the DDL.
+
+If no primary key is defined on the DDL, the connector can only operate in append mode for exchanging INSERT only messages with external system.
+
+Dependencies
+------------
+
+In order to setup the Elasticsearch connector, the following table provides dependency information for both projects using a build automation tool (such as Maven or SBT) and SQL Client with SQL JAR bundles.
+
+| Elasticsearch Version | Maven dependency | SQL Client JAR |
+| :---------------------- | :----------------------------------------------------------------- | :----------------------|
+| 6.x | `flink-connector-elasticsearch6{{site.scala_version_suffix}}` | {% if site.is_stable %} [Download](https://repo.maven.apache.org/maven2/org/apache/flink/flink-sql-connector-elasticsearch6{{site.scala_version_suffix}}/{{site.version}}/flink-sql-connector-elasticsearch6{{site.scala_version_suffix}}-{{site.version}}.jar) {% else %} Only available for [stable releases]({{ site.stable_baseurl }}/zh/dev/table/connectors/elasticsearch.html) {% endif %}|
+| 7.x and later versions | `flink-connector-elasticsearch7{{site.scala_version_suffix}}` | {% if site.is_stable %} [Download](https://repo.maven.apache.org/maven2/org/apache/flink/flink-sql-connector-elasticsearch7{{site.scala_version_suffix}}/{{site.version}}/flink-sql-connector-elasticsearch7{{site.scala_version_suffix}}-{{site.version}}.jar) {% else %} Only available for [stable releases]({{ site.stable_baseurl }}/zh/dev/table/connectors/elasticsearch.html) {% endif %}|
+
+
+Attention Elasticsearch connector works with JSON format which defines how to encode documents for the external system, therefore, it must be added as a [dependency]({% link dev/table/connectors/formats/index.zh.md %}).
+
+How to create an Elasticsearch table
+----------------
+
+The example below shows how to create an Elasticsearch sink table:
+
+
Specify what connector to use, valid values are:
+
+
elasticsearch-6: connect to Elasticsearch 6.x cluster
+
elasticsearch-7: connect to Elasticsearch 7.x and later versions cluster
+
+
+
+
hosts
+
required
+
(none)
+
String
+
One or more Elasticsearch hosts to connect to, e.g. 'http://host_name:9092;http://host_name:9093'.
+
+
+
index
+
required
+
(none)
+
String
+
Elasticsearch index for every record. Can be a static index (e.g. 'myIndex') or
+ a dynamic index (e.g. 'index-{log_ts|yyyy-MM-dd}').
+ See the following Dynamic Index section for more details.
+
+
+
document-type
+
required in 6.x
+
(none)
+
String
+
Elasticsearch document type. Not necessary anymore in elasticsearch-7.
+
+
+
document-id.key-delimiter
+
optional
+
_
+
String
+
Delimiter for composite keys ("_" by default), e.g., "$" would result in IDs "KEY1$KEY2$KEY3"."
+
+
+
failure-handler
+
optional
+
fail
+
String
+
Failure handling strategy in case a request to Elasticsearch fails. Valid strategies are:
+
+
fail: throws an exception if a request fails and thus causes a job failure.
+
ignore: ignores failures and drops the request.
+
retry_rejected: re-adds requests that have failed due to queue capacity saturation.
+
custom class name: for failure handling with a ActionRequestFailureHandler subclass.
+
+
+
+
+
sink.flush-on-checkpoint
+
optional
+
true
+
Boolean
+
Flush on checkpoint or not. When disabled, a sink will not wait for all pending action requests
+ to be acknowledged by Elasticsearch on checkpoints. Thus, a sink does NOT provide any strong
+ guarantees for at-least-once delivery of action requests.
+
+
+
+
sink.bulk-flush.max-actions
+
optional
+
1000
+
Integer
+
Maximum number of buffered actions per bulk request.
+ Can be set to '0' to disable it.
+
+
+
+
sink.bulk-flush.max-size
+
optional
+
2mb
+
MemorySize
+
Maximum size in memory of buffered actions per bulk request. Must be in MB granularity.
+ Can be set to '0' to disable it.
+
+
+
+
sink.bulk-flush.interval
+
optional
+
1s
+
Duration
+
The interval to flush buffered actions.
+ Can be set to '0' to disable it. Note, both 'sink.bulk-flush.max-size' and 'sink.bulk-flush.max-actions'
+ can be set to '0' with the flush interval set allowing for complete async processing of buffered actions.
+
+
+
+
sink.bulk-flush.backoff.strategy
+
optional
+
DISABLED
+
String
+
Specify how to perform retries if any flush actions failed due to a temporary request error. Valid strategies are:
+
+
DISABLED: no retry performed, i.e. fail after the first request error.
+
CONSTANT: wait for backoff delay between retries.
+
EXPONENTIAL: initially wait for backoff delay and increase exponentially between retries.
+
+
+
+
+
sink.bulk-flush.backoff.max-retries
+
optional
+
8
+
Integer
+
Maximum number of backoff retries.
+
+
+
sink.bulk-flush.backoff.delay
+
optional
+
50ms
+
Duration
+
Delay between each backoff attempt. For CONSTANT backoff, this is simply the delay between each retry. For EXPONENTIAL backoff, this is the initial base delay.
+
+
+
connection.max-retry-timeout
+
optional
+
(none)
+
Duration
+
Maximum timeout between retries.
+
+
+
connection.path-prefix
+
optional
+
(none)
+
String
+
Prefix string to be added to every REST communication, e.g., '/v1'
+
+
+
format
+
optional
+
json
+
String
+
Elasticsearch connector supports to specify a format. The format must produce a valid json document.
+ By default uses built-in 'json' format. Please refer to JSON Format page for more details.
+
+
+
+
+
+Features
+----------------
+
+### Key Handling
+
+Elasticsearch sink can work in either upsert mode or append mode, it depends on whether primary key is defined.
+If primary key is defined, Elasticsearch sink works in upsert mode which can consume queries containing UPDATE/DELETE messages.
+If primary key is not defined, Elasticsearch sink works in append mode which can only consume queries containing INSERT only messages.
+
+In Elasticsearch connector, the primary key is used to calculate the Elasticsearch document id, which is a string of up to 512 bytes. It cannot have whitespaces.
+The Elasticsearch connector generates a document ID string for every row by concatenating all primary key fields in the order defined in the DDL using a key delimiter specified by `document-id.key-delimiter`.
+Certain types are not allowed as primary key field as they do not have a good string representation, e.g. `BYTES`, `ROW`, `ARRAY`, `MAP`, etc.
+If no primary key is specified, Elasticsearch will generate a document id automatically.
+
+See [CREATE TABLE DDL]({% link dev/table/sql/create.zh.md %}#create-table) for more details about PRIMARY KEY syntax.
+
+### Dynamic Index
+
+Elasticsearch sink supports both static index and dynamic index.
+
+If you want to have a static index, the `index` option value should be a plain string, e.g. `'myusers'`, all the records will be consistently written into "myusers" index.
+
+If you want to have a dynamic index, you can use `{field_name}` to reference a field value in the record to dynamically generate a target index.
+You can also use `'{field_name|date_format_string}'` to convert a field value of `TIMESTAMP/DATE/TIME` type into the format specified by the `date_format_string`.
+The `date_format_string` is compatible with Java's [DateTimeFormatter](https://docs.oracle.com/javase/8/docs/api/index.html).
+For example, if the option value is `'myusers-{log_ts|yyyy-MM-dd}'`, then a record with `log_ts` field value `2020-03-27 12:25:55` will be written into "myusers-2020-03-27" index.
+
+
+Data Type Mapping
+----------------
+
+Elasticsearch stores document in a JSON string. So the data type mapping is between Flink data type and JSON data type.
+Flink uses built-in `'json'` format for Elasticsearch connector. Please refer to [JSON Format]({% link dev/table/connectors/formats/json.zh.md %}) page for more type mapping details.
+
+{% top %}
diff --git a/docs/dev/table/connectors/filesystem.md b/docs/dev/table/connectors/filesystem.md
new file mode 100644
index 00000000000000..1300bfa510a76f
--- /dev/null
+++ b/docs/dev/table/connectors/filesystem.md
@@ -0,0 +1,374 @@
+---
+title: "FileSystem SQL Connector"
+nav-title: FileSystem
+nav-parent_id: sql-connectors
+nav-pos: 5
+---
+
+
+This connector provides access to partitioned files in filesystems
+supported by the [Flink FileSystem abstraction]({{ site.baseurl}}/ops/filesystems/index.html).
+
+* This will be replaced by the TOC
+{:toc}
+
+The file system connector itself is included in Flink and does not require an additional dependency.
+A corresponding format needs to be specified for reading and writing rows from and to a file system.
+
+The file system connector allows for reading and writing from a local or distributed filesystem. A filesystem table can be defined as:
+
+
+
+{% highlight sql %}
+CREATE TABLE MyUserTable (
+ column_name1 INT,
+ column_name2 STRING,
+ ...
+ part_name1 INT,
+ part_name2 STRING
+) PARTITIONED BY (part_name1, part_name2) WITH (
+ 'connector' = 'filesystem', -- required: specify the connector
+ 'path' = 'file:///path/to/whatever', -- required: path to a directory
+ 'format' = '...', -- required: file system connector requires to specify a format,
+ -- Please refer to Table Formats
+ -- section for more details
+ 'partition.default-name' = '...', -- optional: default partition name in case the dynamic partition
+ -- column value is null/empty string
+
+ -- optional: the option to enable shuffle data by dynamic partition fields in sink phase, this can greatly
+ -- reduce the number of file for filesystem sink but may lead data skew, the default value is false.
+ 'sink.shuffle-by-partition.enable' = '...',
+ ...
+)
+{% endhighlight %}
+
+
+
+Attention Make sure to include [Flink File System specific dependencies]({{ site.baseurl }}/ops/filesystems/index.html).
+
+Attention File system sources for streaming is still under development. In the future, the community will add support for common streaming use cases, i.e., partition and directory monitoring.
+
+Attention The behaviour of file system connector is much different from `previous legacy filesystem connector`:
+the path parameter is specified for a directory not for a file and you can't get a human-readable file in the path that you declare.
+
+## Partition Files
+
+Flink's file system partition support uses the standard hive format. However, it does not require partitions to be pre-registered with a table catalog. Partitions are discovered and inferred based on directory structure. For example, a table partitioned based on the directory below would be inferred to contain `datetime` and `hour` partitions.
+
+```
+path
+└── datetime=2019-08-25
+ └── hour=11
+ ├── part-0.parquet
+ ├── part-1.parquet
+ └── hour=12
+ ├── part-0.parquet
+└── datetime=2019-08-26
+ └── hour=6
+ ├── part-0.parquet
+```
+
+The file system table supports both partition inserting and overwrite inserting. See [INSERT Statement]({{ site.baseurl }}/dev/table/sql/insert.html). When you insert overwrite to a partitioned table, only the corresponding partition will be overwritten, not the entire table.
+
+## File Formats
+
+The file system connector supports multiple formats:
+
+ - CSV: [RFC-4180](https://tools.ietf.org/html/rfc4180). Uncompressed.
+ - JSON: Note JSON format for file system connector is not a typical JSON file but uncompressed [newline delimited JSON](http://jsonlines.org/).
+ - Avro: [Apache Avro](http://avro.apache.org). Support compression by configuring `avro.codec`.
+ - Parquet: [Apache Parquet](http://parquet.apache.org). Compatible with Hive.
+ - Orc: [Apache Orc](http://orc.apache.org). Compatible with Hive.
+
+## Streaming Sink
+
+The file system connector supports streaming writes, based on Flink's [Streaming File Sink]({{ site.baseurl }}/dev/connectors/streamfile_sink.html)
+to write records to file. Row-encoded Formats are csv and json. Bulk-encoded Formats are parquet, orc and avro.
+
+You can write SQL directly, insert the stream data into the non-partitioned table.
+If it is a partitioned table, you can configure partition related operations. See [Partition Commit](filesystem.html#partition-commit) for details.
+
+### Rolling Policy
+
+Data within the partition directories are split into part files. Each partition will contain at least one part file for
+each subtask of the sink that has received data for that partition. The in-progress part file will be closed and additional
+part file will be created according to the configurable rolling policy. The policy rolls part files based on size,
+a timeout that specifies the maximum duration for which a file can be open.
+
+
+
+
+
Key
+
Default
+
Type
+
Description
+
+
+
+
+
sink.rolling-policy.file-size
+
128MB
+
MemorySize
+
The maximum part file size before rolling.
+
+
+
sink.rolling-policy.rollover-interval
+
30 m
+
Duration
+
The maximum time duration a part file can stay open before rolling (by default 30 min to avoid to many small files).
+ The frequency at which this is checked is controlled by the 'sink.rolling-policy.check-interval' option.
+
+
+
sink.rolling-policy.check-interval
+
1 m
+
Duration
+
The interval for checking time based rolling policies. This controls the frequency to check whether a part file should rollover based on 'sink.rolling-policy.rollover-interval'.
+
+
+
+
+**NOTE:** For bulk formats (parquet, orc, avro), the rolling policy in combination with the checkpoint interval(pending files
+become finished on the next checkpoint) control the size and number of these parts.
+
+**NOTE:** For row formats (csv, json), you can set the parameter `sink.rolling-policy.file-size` or `sink.rolling-policy.rollover-interval` in the connector properties and parameter `execution.checkpointing.interval` in flink-conf.yaml together
+if you don't want to wait a long period before observe the data exists in file system. For other formats (avro, orc), you can just set parameter `execution.checkpointing.interval` in flink-conf.yaml.
+
+### Partition Commit
+
+After writing a partition, it is often necessary to notify downstream applications. For example, add the partition to a Hive metastore or writing a `_SUCCESS` file in the directory. The file system sink contains a partition commit feature that allows configuring custom policies. Commit actions are based on a combination of `triggers` and `policies`.
+
+- Trigger: The timing of the commit of the partition can be determined by the watermark with the time extracted from the partition, or by processing time.
+- Policy: How to commit a partition, built-in policies support for the commit of success files and metastore, you can also implement your own policies, such as triggering hive's analysis to generate statistics, or merging small files, etc.
+
+**NOTE:** Partition Commit only works in dynamic partition inserting.
+
+#### Partition commit trigger
+
+To define when to commit a partition, providing partition commit trigger:
+
+
+
+
+
Key
+
Default
+
Type
+
Description
+
+
+
+
+
sink.partition-commit.trigger
+
process-time
+
String
+
Trigger type for partition commit: 'process-time': based on the time of the machine, it neither requires partition time extraction nor watermark generation. Commit partition once the 'current system time' passes 'partition creation system time' plus 'delay'. 'partition-time': based on the time that extracted from partition values, it requires watermark generation. Commit partition once the 'watermark' passes 'time extracted from partition values' plus 'delay'.
+
+
+
sink.partition-commit.delay
+
0 s
+
Duration
+
The partition will not commit until the delay time. If it is a daily partition, should be '1 d', if it is a hourly partition, should be '1 h'.
+
+
+
+
+There are two types of trigger:
+- The first is partition processing time. It neither requires partition time extraction nor watermark
+generation. The trigger of partition commit according to partition creation time and current system time. This trigger
+is more universal, but not so precise. For example, data delay or failover will lead to premature partition commit.
+- The second is the trigger of partition commit according to the time that extracted from partition values and watermark.
+This requires that your job has watermark generation, and the partition is divided according to time, such as
+hourly partition or daily partition.
+
+If you want to let downstream see the partition as soon as possible, no matter whether its data is complete or not:
+- 'sink.partition-commit.trigger'='process-time' (Default value)
+- 'sink.partition-commit.delay'='0s' (Default value)
+Once there is data in the partition, it will immediately commit. Note: the partition may be committed multiple times.
+
+If you want to let downstream see the partition only when its data is complete, and your job has watermark generation, and you can extract the time from partition values:
+- 'sink.partition-commit.trigger'='partition-time'
+- 'sink.partition-commit.delay'='1h' ('1h' if your partition is hourly partition, depends on your partition type)
+This is the most accurate way to commit partition, and it will try to ensure that the committed partitions are as data complete as possible.
+
+If you want to let downstream see the partition only when its data is complete, but there is no watermark, or the time cannot be extracted from partition values:
+- 'sink.partition-commit.trigger'='process-time' (Default value)
+- 'sink.partition-commit.delay'='1h' ('1h' if your partition is hourly partition, depends on your partition type)
+Try to commit partition accurately, but data delay or failover will lead to premature partition commit.
+
+Late data processing: The record will be written into its partition when a record is supposed to be
+written into a partition that has already been committed, and then the committing of this partition
+will be triggered again.
+
+#### Partition Time Extractor
+
+Time extractors define extracting time from partition values.
+
+
+
+
+
Key
+
Default
+
Type
+
Description
+
+
+
+
+
partition.time-extractor.kind
+
default
+
String
+
Time extractor to extract time from partition values. Support default and custom. For default, can configure timestamp pattern. For custom, should configure extractor class.
+
+
+
partition.time-extractor.class
+
(none)
+
String
+
The extractor class for implement PartitionTimeExtractor interface.
+
+
+
partition.time-extractor.timestamp-pattern
+
(none)
+
String
+
The 'default' construction way allows users to use partition fields to get a legal timestamp pattern. Default support 'yyyy-mm-dd hh:mm:ss' from first field. If timestamp should be extracted from a single partition field 'dt', can configure: '$dt'. If timestamp should be extracted from multiple partition fields, say 'year', 'month', 'day' and 'hour', can configure: '$year-$month-$day $hour:00:00'. If timestamp should be extracted from two partition fields 'dt' and 'hour', can configure: '$dt $hour:00:00'.
+
+
+
+
+The default extractor is based on a timestamp pattern composed of your partition fields. You can also specify an implementation for fully custom partition extraction based on the `PartitionTimeExtractor` interface.
+
+
+
+#### Partition Commit Policy
+
+The partition commit policy defines what action is taken when partitions are committed.
+
+- The first is metastore, only hive table supports metastore policy, file system manages partitions through directory structure.
+- The second is the success file, which will write an empty file in the directory corresponding to the partition.
+
+
+
+
+
Key
+
Default
+
Type
+
Description
+
+
+
+
+
sink.partition-commit.policy.kind
+
(none)
+
String
+
Policy to commit a partition is to notify the downstream application that the partition has finished writing, the partition is ready to be read. metastore: add partition to metastore. Only hive table supports metastore policy, file system manages partitions through directory structure. success-file: add '_success' file to directory. Both can be configured at the same time: 'metastore,success-file'. custom: use policy class to create a commit policy. Support to configure multiple policies: 'metastore,success-file'.
+
+
+
sink.partition-commit.policy.class
+
(none)
+
String
+
The partition commit policy class for implement PartitionCommitPolicy interface. Only work in custom commit policy.
+
+
+
sink.partition-commit.success-file.name
+
(none)
+
String
+
The file name for success-file partition commit policy, default is '_SUCCESS'.
+
+
+
+
+You can extend the implementation of commit policy, The custom commit policy implementation like:
+
+
+
+## Full Example
+
+The below shows how the file system connector can be used to write a streaming query to write data from Kafka into a file system and runs a batch query to read that data back out.
+
+{% highlight sql %}
+
+CREATE TABLE kafka_table (
+ user_id STRING,
+ order_amount DOUBLE,
+ log_ts TIMESTAMP(3),
+ WATERMARK FOR log_ts AS log_ts - INTERVAL '5' SECOND
+) WITH (...);
+
+CREATE TABLE fs_table (
+ user_id STRING,
+ order_amount DOUBLE,
+ dt STRING,
+ hour STRING
+) PARTITIONED BY (dt, hour) WITH (
+ 'connector'='filesystem',
+ 'path'='...',
+ 'format'='parquet',
+ 'sink.partition-commit.delay'='1 h',
+ 'sink.partition-commit.policy.kind'='success-file'
+);
+
+-- streaming sql, insert into file system table
+INSERT INTO TABLE fs_table SELECT user_id, order_amount, DATE_FORMAT(log_ts, 'yyyy-MM-dd'), DATE_FORMAT(log_ts, 'HH') FROM kafka_table;
+
+-- batch sql, select with partition pruning
+SELECT * FROM fs_table WHERE dt='2020-05-20' and hour='12';
+
+{% endhighlight %}
+
+{% top %}
diff --git a/docs/dev/table/connectors/filesystem.zh.md b/docs/dev/table/connectors/filesystem.zh.md
new file mode 100644
index 00000000000000..1300bfa510a76f
--- /dev/null
+++ b/docs/dev/table/connectors/filesystem.zh.md
@@ -0,0 +1,374 @@
+---
+title: "FileSystem SQL Connector"
+nav-title: FileSystem
+nav-parent_id: sql-connectors
+nav-pos: 5
+---
+
+
+This connector provides access to partitioned files in filesystems
+supported by the [Flink FileSystem abstraction]({{ site.baseurl}}/ops/filesystems/index.html).
+
+* This will be replaced by the TOC
+{:toc}
+
+The file system connector itself is included in Flink and does not require an additional dependency.
+A corresponding format needs to be specified for reading and writing rows from and to a file system.
+
+The file system connector allows for reading and writing from a local or distributed filesystem. A filesystem table can be defined as:
+
+
+
+{% highlight sql %}
+CREATE TABLE MyUserTable (
+ column_name1 INT,
+ column_name2 STRING,
+ ...
+ part_name1 INT,
+ part_name2 STRING
+) PARTITIONED BY (part_name1, part_name2) WITH (
+ 'connector' = 'filesystem', -- required: specify the connector
+ 'path' = 'file:///path/to/whatever', -- required: path to a directory
+ 'format' = '...', -- required: file system connector requires to specify a format,
+ -- Please refer to Table Formats
+ -- section for more details
+ 'partition.default-name' = '...', -- optional: default partition name in case the dynamic partition
+ -- column value is null/empty string
+
+ -- optional: the option to enable shuffle data by dynamic partition fields in sink phase, this can greatly
+ -- reduce the number of file for filesystem sink but may lead data skew, the default value is false.
+ 'sink.shuffle-by-partition.enable' = '...',
+ ...
+)
+{% endhighlight %}
+
+
+
+Attention Make sure to include [Flink File System specific dependencies]({{ site.baseurl }}/ops/filesystems/index.html).
+
+Attention File system sources for streaming is still under development. In the future, the community will add support for common streaming use cases, i.e., partition and directory monitoring.
+
+Attention The behaviour of file system connector is much different from `previous legacy filesystem connector`:
+the path parameter is specified for a directory not for a file and you can't get a human-readable file in the path that you declare.
+
+## Partition Files
+
+Flink's file system partition support uses the standard hive format. However, it does not require partitions to be pre-registered with a table catalog. Partitions are discovered and inferred based on directory structure. For example, a table partitioned based on the directory below would be inferred to contain `datetime` and `hour` partitions.
+
+```
+path
+└── datetime=2019-08-25
+ └── hour=11
+ ├── part-0.parquet
+ ├── part-1.parquet
+ └── hour=12
+ ├── part-0.parquet
+└── datetime=2019-08-26
+ └── hour=6
+ ├── part-0.parquet
+```
+
+The file system table supports both partition inserting and overwrite inserting. See [INSERT Statement]({{ site.baseurl }}/dev/table/sql/insert.html). When you insert overwrite to a partitioned table, only the corresponding partition will be overwritten, not the entire table.
+
+## File Formats
+
+The file system connector supports multiple formats:
+
+ - CSV: [RFC-4180](https://tools.ietf.org/html/rfc4180). Uncompressed.
+ - JSON: Note JSON format for file system connector is not a typical JSON file but uncompressed [newline delimited JSON](http://jsonlines.org/).
+ - Avro: [Apache Avro](http://avro.apache.org). Support compression by configuring `avro.codec`.
+ - Parquet: [Apache Parquet](http://parquet.apache.org). Compatible with Hive.
+ - Orc: [Apache Orc](http://orc.apache.org). Compatible with Hive.
+
+## Streaming Sink
+
+The file system connector supports streaming writes, based on Flink's [Streaming File Sink]({{ site.baseurl }}/dev/connectors/streamfile_sink.html)
+to write records to file. Row-encoded Formats are csv and json. Bulk-encoded Formats are parquet, orc and avro.
+
+You can write SQL directly, insert the stream data into the non-partitioned table.
+If it is a partitioned table, you can configure partition related operations. See [Partition Commit](filesystem.html#partition-commit) for details.
+
+### Rolling Policy
+
+Data within the partition directories are split into part files. Each partition will contain at least one part file for
+each subtask of the sink that has received data for that partition. The in-progress part file will be closed and additional
+part file will be created according to the configurable rolling policy. The policy rolls part files based on size,
+a timeout that specifies the maximum duration for which a file can be open.
+
+
+
+
+
Key
+
Default
+
Type
+
Description
+
+
+
+
+
sink.rolling-policy.file-size
+
128MB
+
MemorySize
+
The maximum part file size before rolling.
+
+
+
sink.rolling-policy.rollover-interval
+
30 m
+
Duration
+
The maximum time duration a part file can stay open before rolling (by default 30 min to avoid to many small files).
+ The frequency at which this is checked is controlled by the 'sink.rolling-policy.check-interval' option.
+
+
+
sink.rolling-policy.check-interval
+
1 m
+
Duration
+
The interval for checking time based rolling policies. This controls the frequency to check whether a part file should rollover based on 'sink.rolling-policy.rollover-interval'.
+
+
+
+
+**NOTE:** For bulk formats (parquet, orc, avro), the rolling policy in combination with the checkpoint interval(pending files
+become finished on the next checkpoint) control the size and number of these parts.
+
+**NOTE:** For row formats (csv, json), you can set the parameter `sink.rolling-policy.file-size` or `sink.rolling-policy.rollover-interval` in the connector properties and parameter `execution.checkpointing.interval` in flink-conf.yaml together
+if you don't want to wait a long period before observe the data exists in file system. For other formats (avro, orc), you can just set parameter `execution.checkpointing.interval` in flink-conf.yaml.
+
+### Partition Commit
+
+After writing a partition, it is often necessary to notify downstream applications. For example, add the partition to a Hive metastore or writing a `_SUCCESS` file in the directory. The file system sink contains a partition commit feature that allows configuring custom policies. Commit actions are based on a combination of `triggers` and `policies`.
+
+- Trigger: The timing of the commit of the partition can be determined by the watermark with the time extracted from the partition, or by processing time.
+- Policy: How to commit a partition, built-in policies support for the commit of success files and metastore, you can also implement your own policies, such as triggering hive's analysis to generate statistics, or merging small files, etc.
+
+**NOTE:** Partition Commit only works in dynamic partition inserting.
+
+#### Partition commit trigger
+
+To define when to commit a partition, providing partition commit trigger:
+
+
+
+
+
Key
+
Default
+
Type
+
Description
+
+
+
+
+
sink.partition-commit.trigger
+
process-time
+
String
+
Trigger type for partition commit: 'process-time': based on the time of the machine, it neither requires partition time extraction nor watermark generation. Commit partition once the 'current system time' passes 'partition creation system time' plus 'delay'. 'partition-time': based on the time that extracted from partition values, it requires watermark generation. Commit partition once the 'watermark' passes 'time extracted from partition values' plus 'delay'.
+
+
+
sink.partition-commit.delay
+
0 s
+
Duration
+
The partition will not commit until the delay time. If it is a daily partition, should be '1 d', if it is a hourly partition, should be '1 h'.
+
+
+
+
+There are two types of trigger:
+- The first is partition processing time. It neither requires partition time extraction nor watermark
+generation. The trigger of partition commit according to partition creation time and current system time. This trigger
+is more universal, but not so precise. For example, data delay or failover will lead to premature partition commit.
+- The second is the trigger of partition commit according to the time that extracted from partition values and watermark.
+This requires that your job has watermark generation, and the partition is divided according to time, such as
+hourly partition or daily partition.
+
+If you want to let downstream see the partition as soon as possible, no matter whether its data is complete or not:
+- 'sink.partition-commit.trigger'='process-time' (Default value)
+- 'sink.partition-commit.delay'='0s' (Default value)
+Once there is data in the partition, it will immediately commit. Note: the partition may be committed multiple times.
+
+If you want to let downstream see the partition only when its data is complete, and your job has watermark generation, and you can extract the time from partition values:
+- 'sink.partition-commit.trigger'='partition-time'
+- 'sink.partition-commit.delay'='1h' ('1h' if your partition is hourly partition, depends on your partition type)
+This is the most accurate way to commit partition, and it will try to ensure that the committed partitions are as data complete as possible.
+
+If you want to let downstream see the partition only when its data is complete, but there is no watermark, or the time cannot be extracted from partition values:
+- 'sink.partition-commit.trigger'='process-time' (Default value)
+- 'sink.partition-commit.delay'='1h' ('1h' if your partition is hourly partition, depends on your partition type)
+Try to commit partition accurately, but data delay or failover will lead to premature partition commit.
+
+Late data processing: The record will be written into its partition when a record is supposed to be
+written into a partition that has already been committed, and then the committing of this partition
+will be triggered again.
+
+#### Partition Time Extractor
+
+Time extractors define extracting time from partition values.
+
+
+
+
+
Key
+
Default
+
Type
+
Description
+
+
+
+
+
partition.time-extractor.kind
+
default
+
String
+
Time extractor to extract time from partition values. Support default and custom. For default, can configure timestamp pattern. For custom, should configure extractor class.
+
+
+
partition.time-extractor.class
+
(none)
+
String
+
The extractor class for implement PartitionTimeExtractor interface.
+
+
+
partition.time-extractor.timestamp-pattern
+
(none)
+
String
+
The 'default' construction way allows users to use partition fields to get a legal timestamp pattern. Default support 'yyyy-mm-dd hh:mm:ss' from first field. If timestamp should be extracted from a single partition field 'dt', can configure: '$dt'. If timestamp should be extracted from multiple partition fields, say 'year', 'month', 'day' and 'hour', can configure: '$year-$month-$day $hour:00:00'. If timestamp should be extracted from two partition fields 'dt' and 'hour', can configure: '$dt $hour:00:00'.
+
+
+
+
+The default extractor is based on a timestamp pattern composed of your partition fields. You can also specify an implementation for fully custom partition extraction based on the `PartitionTimeExtractor` interface.
+
+
+
+#### Partition Commit Policy
+
+The partition commit policy defines what action is taken when partitions are committed.
+
+- The first is metastore, only hive table supports metastore policy, file system manages partitions through directory structure.
+- The second is the success file, which will write an empty file in the directory corresponding to the partition.
+
+
+
+
+
Key
+
Default
+
Type
+
Description
+
+
+
+
+
sink.partition-commit.policy.kind
+
(none)
+
String
+
Policy to commit a partition is to notify the downstream application that the partition has finished writing, the partition is ready to be read. metastore: add partition to metastore. Only hive table supports metastore policy, file system manages partitions through directory structure. success-file: add '_success' file to directory. Both can be configured at the same time: 'metastore,success-file'. custom: use policy class to create a commit policy. Support to configure multiple policies: 'metastore,success-file'.
+
+
+
sink.partition-commit.policy.class
+
(none)
+
String
+
The partition commit policy class for implement PartitionCommitPolicy interface. Only work in custom commit policy.
+
+
+
sink.partition-commit.success-file.name
+
(none)
+
String
+
The file name for success-file partition commit policy, default is '_SUCCESS'.
+
+
+
+
+You can extend the implementation of commit policy, The custom commit policy implementation like:
+
+
+
+## Full Example
+
+The below shows how the file system connector can be used to write a streaming query to write data from Kafka into a file system and runs a batch query to read that data back out.
+
+{% highlight sql %}
+
+CREATE TABLE kafka_table (
+ user_id STRING,
+ order_amount DOUBLE,
+ log_ts TIMESTAMP(3),
+ WATERMARK FOR log_ts AS log_ts - INTERVAL '5' SECOND
+) WITH (...);
+
+CREATE TABLE fs_table (
+ user_id STRING,
+ order_amount DOUBLE,
+ dt STRING,
+ hour STRING
+) PARTITIONED BY (dt, hour) WITH (
+ 'connector'='filesystem',
+ 'path'='...',
+ 'format'='parquet',
+ 'sink.partition-commit.delay'='1 h',
+ 'sink.partition-commit.policy.kind'='success-file'
+);
+
+-- streaming sql, insert into file system table
+INSERT INTO TABLE fs_table SELECT user_id, order_amount, DATE_FORMAT(log_ts, 'yyyy-MM-dd'), DATE_FORMAT(log_ts, 'HH') FROM kafka_table;
+
+-- batch sql, select with partition pruning
+SELECT * FROM fs_table WHERE dt='2020-05-20' and hour='12';
+
+{% endhighlight %}
+
+{% top %}
diff --git a/docs/dev/table/connectors/formats/avro.md b/docs/dev/table/connectors/formats/avro.md
new file mode 100644
index 00000000000000..017c96c27dbeeb
--- /dev/null
+++ b/docs/dev/table/connectors/formats/avro.md
@@ -0,0 +1,218 @@
+---
+title: "Avro Format"
+nav-title: Avro
+nav-parent_id: sql-formats
+nav-pos: 3
+---
+
+
+Format: Serialization Schema
+Format: Deserialization Schema
+
+* This will be replaced by the TOC
+{:toc}
+
+The [Apache Avro](https://avro.apache.org/) format allows to read and write Avro data based on an Avro schema. Currently, the Avro schema is derived from table schema.
+
+Dependencies
+------------
+
+In order to setup the Avro format, the following table provides dependency information for both projects using a build automation tool (such as Maven or SBT) and SQL Client with SQL JAR bundles.
+
+
+
+You can download flink-avro from [Download](https://repo.maven.apache.org/maven2/org/apache/flink/flink-avro/{{site.version}}/flink-avro-{{site.version}}-sql-jar.jar),
+and requires additional [Hadoop dependency]({% link ops/deployment/hadoop.md %}) for cluster execution.
+
Specify what format to use, here should be 'avro'.
+
+
+
avro.codec
+
optional
+
(none)
+
String
+
For Filesystem only, the compression codec for avro. No compression as default. The valid enumerations are: deflate, snappy, bzip2, xz.
+
+
+
+
+Data Type Mapping
+----------------
+
+Currently, the Avro schema is always derived from table schema. Explicitly defining an Avro schema is not supported yet.
+So the following table lists the type mapping from Flink type to Avro type.
+
+
+
+
+
Flink SQL type
+
Avro type
+
Avro logical type
+
+
+
+
+
CHAR / VARCHAR / STRING
+
string
+
+
+
+
BOOLEAN
+
boolean
+
+
+
+
BINARY / VARBINARY
+
bytes
+
+
+
+
DECIMAL
+
fixed
+
decimal
+
+
+
TINYINT
+
int
+
+
+
+
SMALLINT
+
int
+
+
+
+
INT
+
int
+
+
+
+
BIGINT
+
long
+
+
+
+
FLOAT
+
float
+
+
+
+
DOUBLE
+
double
+
+
+
+
DATE
+
int
+
date
+
+
+
TIME
+
int
+
time-millis
+
+
+
TIMESTAMP
+
long
+
timestamp-millis
+
+
+
ARRAY
+
array
+
+
+
+
MAP
+ (key must be string/char/varchar type)
+
map
+
+
+
+
MULTISET
+ (element must be string/char/varchar type)
+
map
+
+
+
+
ROW
+
record
+
+
+
+
+
+In addition to the types listed above, Flink supports reading/writing nullable types. Flink maps nullable types to Avro `union(something, null)`, where `something` is the Avro type converted from Flink type.
+
+You can refer to [Avro Specification](https://avro.apache.org/docs/current/spec.html) for more information about Avro types.
diff --git a/docs/dev/table/connectors/formats/avro.zh.md b/docs/dev/table/connectors/formats/avro.zh.md
new file mode 100644
index 00000000000000..89cbfc9ede3d47
--- /dev/null
+++ b/docs/dev/table/connectors/formats/avro.zh.md
@@ -0,0 +1,217 @@
+---
+title: "Avro Format"
+nav-title: Avro
+nav-parent_id: sql-formats
+nav-pos: 3
+---
+
+
+Format: Serialization Schema
+Format: Deserialization Schema
+
+* This will be replaced by the TOC
+{:toc}
+
+[Apache Avro](https://avro.apache.org/) format 允许基于 Avro schema 读取和写入 Avro 数据。目前,Avro schema 从 table schema 推导而来。
+
+依赖
+------------
+
+为了设置 Avro format,下表提供了使用自动化构建工具(例如 Maven 或 SBT)和带有 SQL JAR 捆绑包的 SQL Client 的项目依赖信息。
+
+
+
+除了上面列出的类型,Flink 支持读取/写入 nullable 的类型。Flink 将 nullable 的类型映射到 Avro `union(something, null)`,其中 `something` 是从 Flink 类型转换的 Avro 类型。
+
+您可以参考 [Avro 规范](https://avro.apache.org/docs/current/spec.html) 获取更多有关 Avro 类型的信息。
diff --git a/docs/dev/table/connectors/formats/canal.md b/docs/dev/table/connectors/formats/canal.md
new file mode 100644
index 00000000000000..6f9ea44de9d5d8
--- /dev/null
+++ b/docs/dev/table/connectors/formats/canal.md
@@ -0,0 +1,191 @@
+---
+title: "Canal Format"
+nav-title: Canal
+nav-parent_id: sql-formats
+nav-pos: 5
+---
+
+
+Changelog-Data-Capture Format
+Format: Deserialization Schema
+
+* This will be replaced by the TOC
+{:toc}
+
+[Canal](https://github.com/alibaba/canal/wiki) is a CDC (Changelog Data Capture) tool that can stream changes in real-time from MySQL into other systems. Canal provides a unified format schema for changelog and supports to serialize messages using JSON and [protobuf](https://developers.google.com/protocol-buffers) (protobuf is the default format for Canal).
+
+Flink supports to interpret Canal JSON messages as INSERT/UPDATE/DELETE messages into Flink SQL system. This is useful in many cases to leverage this feature, such as
+ - synchronizing incremental data from databases to other systems
+ - auditing logs
+ - real-time materialized views on databases
+ - temporal join changing history of a database table and so on.
+
+*Note: Support for interpreting Canal protobuf messages and emitting Canal messages is on the roadmap.*
+
+Dependencies
+------------
+
+In order to setup the Canal format, the following table provides dependency information for both projects using a build automation tool (such as Maven or SBT) and SQL Client with SQL JAR bundles.
+
+| Maven dependency | SQL Client JAR |
+| :----------------- | :----------------------|
+| `flink-json` | Built-in |
+
+*Note: please refer to [Canal documentation](https://github.com/alibaba/canal/wiki) about how to deploy Canal to synchronize changelog to message queues.*
+
+
+How to use Canal format
+----------------
+
+Canal provides a unified format for changelog, here is a simple example for an update operation captured from a MySQL `products` table:
+
+```json
+{
+ "data": [
+ {
+ "id": "111",
+ "name": "scooter",
+ "description": "Big 2-wheel scooter",
+ "weight": "5.18"
+ }
+ ],
+ "database": "inventory",
+ "es": 1589373560000,
+ "id": 9,
+ "isDdl": false,
+ "mysqlType": {
+ "id": "INTEGER",
+ "name": "VARCHAR(255)",
+ "description": "VARCHAR(512)",
+ "weight": "FLOAT"
+ },
+ "old": [
+ {
+ "weight": "5.15"
+ }
+ ],
+ "pkNames": [
+ "id"
+ ],
+ "sql": "",
+ "sqlType": {
+ "id": 4,
+ "name": 12,
+ "description": 12,
+ "weight": 7
+ },
+ "table": "products",
+ "ts": 1589373560798,
+ "type": "UPDATE"
+}
+```
+
+*Note: please refer to [Canal documentation](https://github.com/alibaba/canal/wiki) about the meaning of each fields.*
+
+The MySQL `products` table has 4 columns (`id`, `name`, `description` and `weight`). The above JSON message is an update change event on the `products` table where the `weight` value of the row with `id = 111` is changed from `5.18` to `5.15`.
+Assuming the messages have been synchronized to Kafka topic `products_binlog`, then we can use the following DDL to consume this topic and interpret the change events.
+
+
+
+{% highlight sql %}
+CREATE TABLE topic_products (
+ -- schema is totally the same to the MySQL "products" table
+ id BIGINT,
+ name STRING,
+ description STRING,
+ weight DECIMAL(10, 2)
+) WITH (
+ 'connector' = 'kafka',
+ 'topic' = 'products_binlog',
+ 'properties.bootstrap.servers' = 'localhost:9092',
+ 'properties.group.id' = 'testGroup',
+ 'format' = 'canal-json' -- using canal-json as the format
+)
+{% endhighlight %}
+
+
+
+After registering the topic as a Flink table, you can consume the Canal messages as a changelog source.
+
+
+
+{% highlight sql %}
+-- a real-time materialized view on the MySQL "products"
+-- which calculates the latest average of weight for the same products
+SELECT name, AVG(weight) FROM topic_products GROUP BY name;
+
+-- synchronize all the data and incremental changes of MySQL "products" table to
+-- Elasticsearch "products" index for future searching
+INSERT INTO elasticsearch_products
+SELECT * FROM topic_products;
+{% endhighlight %}
+
+
+
+
+Format Options
+----------------
+
+
+
+
+
Option
+
Required
+
Default
+
Type
+
Description
+
+
+
+
+
format
+
required
+
(none)
+
String
+
Specify what format to use, here should be 'canal-json'.
+
+
+
canal-json.ignore-parse-errors
+
optional
+
false
+
Boolean
+
Skip fields and rows with parse errors instead of failing.
+ Fields are set to null in case of errors.
+
+
+
canal-json.timestamp-format.standard
+
optional
+
'SQL'
+
String
+
Specify the input and output timestamp format. Currently supported values are 'SQL' and 'ISO-8601':
+
+
Option 'SQL' will parse input timestamp in "yyyy-MM-dd HH:mm:ss.s{precision}" format, e.g '2020-12-30 12:13:14.123' and output timestamp in the same format.
+
Option 'ISO-8601'will parse input timestamp in "yyyy-MM-ddTHH:mm:ss.s{precision}" format, e.g '2020-12-30T12:13:14.123' and output timestamp in the same format.
+
+数据类型映射
+----------------
+
+目前,Canal Format 使用 JSON Format 进行反序列化。 有关数据类型映射的更多详细信息,请参阅 [JSON Format 文档]({% link dev/table/connectors/formats/json.zh.md %}#data-type-mapping)。
+
diff --git a/docs/dev/table/connectors/formats/csv.md b/docs/dev/table/connectors/formats/csv.md
new file mode 100644
index 00000000000000..b34a1e839a569c
--- /dev/null
+++ b/docs/dev/table/connectors/formats/csv.md
@@ -0,0 +1,253 @@
+---
+title: "CSV Format"
+nav-title: CSV
+nav-parent_id: sql-formats
+nav-pos: 1
+---
+
+
+Format: Serialization Schema
+Format: Deserialization Schema
+
+* This will be replaced by the TOC
+{:toc}
+
+The [CSV](https://en.wikipedia.org/wiki/Comma-separated_values) format allows to read and write CSV data based on an CSV schema. Currently, the CSV schema is derived from table schema.
+
+Dependencies
+------------
+
+In order to setup the CSV format, the following table provides dependency information for both projects using a build automation tool (such as Maven or SBT) and SQL Client with SQL JAR bundles.
+
+| Maven dependency | SQL Client JAR |
+| :----------------- | :----------------------|
+| `flink-csv` | Built-in |
+
+How to create a table with CSV format
+----------------
+
+Here is an example to create a table using Kafka connector and CSV format.
+
+
Line delimiter, \n by default. Note the \n and \r are invisible special characters, you have to use unicode to specify them in plain SQL.
+
+
e.g. 'csv.line-delimiter' = U&'\\000D' specifies the to use carriage return \r as line delimiter.
+
e.g. 'csv.line-delimiter' = U&'\\000A' specifies the to use line feed \n as line delimiter.
+
+
+
+
+
csv.disable-quote-character
+
optional
+
false
+
Boolean
+
Disabled quote character for enclosing field values (false by default).
+ If true, option 'csv.quote-character' must be set.
+
+
+
csv.quote-character
+
optional
+
"
+
String
+
Quote character for enclosing field values (" by default).
+
+
+
csv.allow-comments
+
optional
+
false
+
Boolean
+
Ignore comment lines that start with '#' (disabled by default).
+ If enabled, make sure to also ignore parse errors to allow empty rows.
+
+
+
csv.ignore-parse-errors
+
optional
+
false
+
Boolean
+
Skip fields and rows with parse errors instead of failing.
+ Fields are set to null in case of errors.
+
+
+
csv.array-element-delimiter
+
optional
+
;
+
String
+
Array element delimiter string for separating
+ array and row element values (';' by default).
+
+
+
csv.escape-character
+
optional
+
(none)
+
String
+
Escape character for escaping values (disabled by default).
+
+
+
csv.null-literal
+
optional
+
(none)
+
String
+
Null literal string that is interpreted as a null value (disabled by default).
+
+
+
+
+Data Type Mapping
+----------------
+
+Currently, the CSV schema is always derived from table schema. Explicitly defining an CSV schema is not supported yet.
+
+Flink CSV format uses [jackson databind API](https://github.com/FasterXML/jackson-databind) to parse and generate CSV string.
+
+The following table lists the type mapping from Flink type to CSV type.
+
+
diff --git a/docs/dev/table/connectors/formats/debezium.md b/docs/dev/table/connectors/formats/debezium.md
new file mode 100644
index 00000000000000..98acf5f4621556
--- /dev/null
+++ b/docs/dev/table/connectors/formats/debezium.md
@@ -0,0 +1,207 @@
+---
+title: "Debezium Format"
+nav-title: Debezium
+nav-parent_id: sql-formats
+nav-pos: 4
+---
+
+
+Changelog-Data-Capture Format
+Format: Deserialization Schema
+
+* This will be replaced by the TOC
+{:toc}
+
+[Debezium](https://debezium.io/) is a CDC (Changelog Data Capture) tool that can stream changes in real-time from MySQL, PostgreSQL, Oracle, Microsoft SQL Server and many other databases into Kafka. Debezium provides a unified format schema for changelog and supports to serialize messages using JSON and [Apache Avro](https://avro.apache.org/).
+
+Flink supports to interpret Debezium JSON messages as INSERT/UPDATE/DELETE messages into Flink SQL system. This is useful in many cases to leverage this feature, such as
+ - synchronizing incremental data from databases to other systems
+ - auditing logs
+ - real-time materialized views on databases
+ - temporal join changing history of a database table and so on.
+
+*Note: Support for interpreting Debezium Avro messages and emitting Debezium messages is on the roadmap.*
+
+Dependencies
+------------
+
+In order to setup the Debezium format, the following table provides dependency information for both projects using a build automation tool (such as Maven or SBT) and SQL Client with SQL JAR bundles.
+
+| Maven dependency | SQL Client JAR |
+| :----------------- | :----------------------|
+| `flink-json` | Built-in |
+
+*Note: please refer to [Debezium documentation](https://debezium.io/documentation/reference/1.1/index.html) about how to setup a Debezium Kafka Connect to synchronize changelog to Kafka topics.*
+
+
+How to use Debezium format
+----------------
+
+Debezium provides a unified format for changelog, here is a simple example for an update operation captured from a MySQL `products` table:
+
+```json
+{
+ "before": {
+ "id": 111,
+ "name": "scooter",
+ "description": "Big 2-wheel scooter",
+ "weight": 5.18
+ },
+ "after": {
+ "id": 111,
+ "name": "scooter",
+ "description": "Big 2-wheel scooter",
+ "weight": 5.15
+ },
+ "source": {...},
+ "op": "u",
+ "ts_ms": 1589362330904,
+ "transaction": null
+}
+```
+
+*Note: please refer to [Debezium documentation](https://debezium.io/documentation/reference/1.1/connectors/mysql.html#mysql-connector-events_debezium) about the meaning of each fields.*
+
+The MySQL `products` table has 4 columns (`id`, `name`, `description` and `weight`). The above JSON message is an update change event on the `products` table where the `weight` value of the row with `id = 111` is changed from `5.18` to `5.15`.
+Assuming this messages is synchronized to Kafka topic `products_binlog`, then we can use the following DDL to consume this topic and interpret the change events.
+
+
+
+{% highlight sql %}
+CREATE TABLE topic_products (
+ -- schema is totally the same to the MySQL "products" table
+ id BIGINT,
+ name STRING,
+ description STRING,
+ weight DECIMAL(10, 2)
+) WITH (
+ 'connector' = 'kafka',
+ 'topic' = 'products_binlog',
+ 'properties.bootstrap.servers' = 'localhost:9092',
+ 'properties.group.id' = 'testGroup',
+ 'format' = 'debezium-json' -- using debezium-json as the format
+)
+{% endhighlight %}
+
+
+
+In some cases, users may setup the Debezium Kafka Connect with the Kafka configuration `'value.converter.schemas.enable'` enabled to include schema in the message. Then the Debezium JSON message may look like this:
+
+```json
+{
+ "schema": {...},
+ "payload": {
+ "before": {
+ "id": 111,
+ "name": "scooter",
+ "description": "Big 2-wheel scooter",
+ "weight": 5.18
+ },
+ "after": {
+ "id": 111,
+ "name": "scooter",
+ "description": "Big 2-wheel scooter",
+ "weight": 5.15
+ },
+ "source": {...},
+ "op": "u",
+ "ts_ms": 1589362330904,
+ "transaction": null
+ }
+}
+```
+
+In order to interpret such messages, you need to add the option `'debezium-json.schema-include' = 'true'` into above DDL WITH clause (`false` by default). Usually, this is not recommended to include schema because this makes the messages very verbose and reduces parsing performance.
+
+After registering the topic as a Flink table, then you can consume the Debezium messages as a changelog source.
+
+
+
+{% highlight sql %}
+-- a real-time materialized view on the MySQL "products"
+-- which calculate the latest average of weight for the same products
+SELECT name, AVG(weight) FROM topic_products GROUP BY name;
+
+-- synchronize all the data and incremental changes of MySQL "products" table to
+-- Elasticsearch "products" index for future searching
+INSERT INTO elasticsearch_products
+SELECT * FROM topic_products;
+{% endhighlight %}
+
+
+
+
+Format Options
+----------------
+
+
+
+
+
Option
+
Required
+
Default
+
Type
+
Description
+
+
+
+
+
format
+
required
+
(none)
+
String
+
Specify what format to use, here should be 'debezium-json'.
+
+
+
debezium-json.schema-include
+
optional
+
false
+
Boolean
+
When setting up a Debezium Kafka Connect, users may enable a Kafka configuration 'value.converter.schemas.enable' to include schema in the message.
+ This option indicates whether the Debezium JSON message includes the schema or not.
+
+
+
debezium-json.ignore-parse-errors
+
optional
+
false
+
Boolean
+
Skip fields and rows with parse errors instead of failing.
+ Fields are set to null in case of errors.
+
+
+
debezium-json.timestamp-format.standard
+
optional
+
'SQL'
+
String
+
Specify the input and output timestamp format. Currently supported values are 'SQL' and 'ISO-8601':
+
+
Option 'SQL' will parse input timestamp in "yyyy-MM-dd HH:mm:ss.s{precision}" format, e.g '2020-12-30 12:13:14.123' and output timestamp in the same format.
+
Option 'ISO-8601'will parse input timestamp in "yyyy-MM-ddTHH:mm:ss.s{precision}" format, e.g '2020-12-30T12:13:14.123' and output timestamp in the same format.
+
+
+
+
+
+
+Data Type Mapping
+----------------
+
+Currently, the Debezium format uses JSON format for deserialization. Please refer to [JSON format documentation]({% link dev/table/connectors/formats/json.md %}#data-type-mapping) for more details about the data type mapping.
+
diff --git a/docs/dev/table/connectors/formats/debezium.zh.md b/docs/dev/table/connectors/formats/debezium.zh.md
new file mode 100644
index 00000000000000..8ffdbd21200614
--- /dev/null
+++ b/docs/dev/table/connectors/formats/debezium.zh.md
@@ -0,0 +1,205 @@
+---
+title: "Debezium Format"
+nav-title: Debezium
+nav-parent_id: sql-formats
+nav-pos: 4
+---
+
+
+Changelog-Data-Capture Format
+Format: Deserialization Schema
+
+* This will be replaced by the TOC
+{:toc}
+
+[Debezium](https://debezium.io/) 是一个 CDC(Changelog Data Capture,变更数据捕获)的工具,可以把来自 MySQL、PostgreSQL、Oracle、Microsoft SQL Server 和许多其他数据库的更改实时流式传输到 Kafka 中。 Debezium 为变更日志提供了统一的格式结构,并支持使用 JSON 和 Apache Avro 序列化消息。
+
+Flink 支持将 Debezium JSON 消息解析为 INSERT / UPDATE / DELETE 消息到 Flink SQL 系统中。在很多情况下,利用这个特性非常的有用,例如
+ - 将增量数据从数据库同步到其他系统
+ - 日志审计
+ - 数据库的实时物化视图
+ - 关联维度数据库的变更历史,等等。
+
+*注意: 支持解析 Debezium Avro 消息和输出 Debezium 消息已经规划在路线图上了。*
+
+依赖
+------------
+
+为了设置 Debezium Format,下表提供了使用构建自动化工具(例如 Maven 或 SBT)和带有 SQL JAR 包的 SQL Client 的两个项目的依赖项信息。
+
+| Maven 依赖 | SQL Client JAR |
+| :----------------- | :----------------------|
+| `flink-json` | 内置 |
+
+*注意: 请参考 [Debezium 文档](https://debezium.io/documentation/reference/1.1/index.html),了解如何设置 Debezium Kafka Connect 用来将变更日志同步到 Kafka 主题。*
+
+
+如何使用 Debezium Format
+----------------
+
+
+Debezium 为变更日志提供了统一的格式,这是一个从 MySQL product 表捕获的更新操作的简单示例:
+
+```json
+{
+ "before": {
+ "id": 111,
+ "name": "scooter",
+ "description": "Big 2-wheel scooter",
+ "weight": 5.18
+ },
+ "after": {
+ "id": 111,
+ "name": "scooter",
+ "description": "Big 2-wheel scooter",
+ "weight": 5.15
+ },
+ "source": {...},
+ "op": "u",
+ "ts_ms": 1589362330904,
+ "transaction": null
+}
+```
+
+*注意: 请参考 [Debezium 文档](https://debezium.io/documentation/reference/1.1/connectors/mysql.html#mysql-connector-events_debezium),了解每个字段的含义。*
+
+MySQL 产品表有4列(`id`、`name`、`description`、`weight`)。上面的 JSON 消息是 `products` 表上的一条更新事件,其中 `id = 111` 的行的 `weight` 值从 `5.18` 更改为 `5.15`。假设此消息已同步到 Kafka 主题 `products_binlog`,则可以使用以下 DDL 来使用此主题并解析更改事件。
+
+
+
+数据类型映射
+----------------
+
+目前,Debezium Format 使用 JSON Format 进行反序列化。有关数据类型映射的更多详细信息,请参考 [JSON Format 文档]({% link dev/table/connectors/formats/json.zh.md %}#data-type-mapping)。
+
diff --git a/docs/dev/table/connectors/formats/index.md b/docs/dev/table/connectors/formats/index.md
new file mode 100644
index 00000000000000..66f9bc87bc262c
--- /dev/null
+++ b/docs/dev/table/connectors/formats/index.md
@@ -0,0 +1,72 @@
+---
+title: "Formats"
+nav-id: sql-formats
+nav-parent_id: sql-connectors
+nav-pos: 1
+nav-show_overview: true
+---
+
+
+Flink provides a set of table formats that can be used with table connectors. A table format is a storage format defines how to map binary data onto table columns.
+
+Flink supports the following formats:
+
+
diff --git a/docs/dev/table/connectors/formats/json.md b/docs/dev/table/connectors/formats/json.md
new file mode 100644
index 00000000000000..181f48e291186c
--- /dev/null
+++ b/docs/dev/table/connectors/formats/json.md
@@ -0,0 +1,212 @@
+---
+title: "JSON Format"
+nav-title: JSON
+nav-parent_id: sql-formats
+nav-pos: 2
+---
+
+
+Format: Serialization Schema
+Format: Deserialization Schema
+
+* This will be replaced by the TOC
+{:toc}
+
+The [JSON](https://www.json.org/json-en.html) format allows to read and write JSON data based on an JSON schema. Currently, the JSON schema is derived from table schema.
+
+Dependencies
+------------
+
+In order to setup the JSON format, the following table provides dependency information for both projects using a build automation tool (such as Maven or SBT) and SQL Client with SQL JAR bundles.
+
+| Maven dependency | SQL Client JAR |
+| :----------------- | :----------------------|
+| `flink-json` | Built-in |
+
+How to create a table with JSON format
+----------------
+
+Here is an example to create a table using Kafka connector and JSON format.
+
+
Specify what format to use, here should be 'json'.
+
+
+
json.fail-on-missing-field
+
optional
+
false
+
Boolean
+
Whether to fail if a field is missing or not.
+
+
+
json.ignore-parse-errors
+
optional
+
false
+
Boolean
+
Skip fields and rows with parse errors instead of failing.
+ Fields are set to null in case of errors.
+
+
+
json.timestamp-format.standard
+
optional
+
'SQL'
+
String
+
Specify the input and output timestamp format. Currently supported values are 'SQL' and 'ISO-8601':
+
+
Option 'SQL' will parse input timestamp in "yyyy-MM-dd HH:mm:ss.s{precision}" format, e.g '2020-12-30 12:13:14.123' and output timestamp in the same format.
+
Option 'ISO-8601'will parse input timestamp in "yyyy-MM-ddTHH:mm:ss.s{precision}" format, e.g '2020-12-30T12:13:14.123' and output timestamp in the same format.
+
+
+
+
+
+
+Data Type Mapping
+----------------
+
+Currently, the JSON schema is always derived from table schema. Explicitly defining an JSON schema is not supported yet.
+
+Flink JSON format uses [jackson databind API](https://github.com/FasterXML/jackson-databind) to parse and generate JSON string.
+
+The following table lists the type mapping from Flink type to JSON type.
+
+
+
+
+
Flink SQL type
+
JSON type
+
+
+
+
+
CHAR / VARCHAR / STRING
+
string
+
+
+
BOOLEAN
+
boolean
+
+
+
BINARY / VARBINARY
+
string with encoding: base64
+
+
+
DECIMAL
+
number
+
+
+
TINYINT
+
number
+
+
+
SMALLINT
+
number
+
+
+
INT
+
number
+
+
+
BIGINT
+
number
+
+
+
FLOAT
+
number
+
+
+
DOUBLE
+
number
+
+
+
DATE
+
string with format: date
+
+
+
TIME
+
string with format: time
+
+
+
TIMESTAMP
+
string with format: date-time
+
+
+
INTERVAL
+
number
+
+
+
ARRAY
+
array
+
+
+
MAP / MULTISET
+
object
+
+
+
ROW
+
object
+
+
+
+
+
+
+
+
diff --git a/docs/dev/table/connectors/formats/json.zh.md b/docs/dev/table/connectors/formats/json.zh.md
new file mode 100644
index 00000000000000..cf8f2863f921c1
--- /dev/null
+++ b/docs/dev/table/connectors/formats/json.zh.md
@@ -0,0 +1,211 @@
+---
+title: "JSON Format"
+nav-title: JSON
+nav-parent_id: sql-formats
+nav-pos: 2
+---
+
+
+Format: Serialization Schema
+Format: Deserialization Schema
+
+* This will be replaced by the TOC
+{:toc}
+
+[JSON](https://www.json.org/json-en.html) Format 能读写 JSON 格式的数据。当前,JSON schema 是从 table schema 中自动推导而得的。
+
+依赖
+------------
+
+为了使用 JSON format,下表列出了利用自动化构建工具(例如,Maven 或者 SBT )构建项目以及 SQL Client 所需要的依赖。
+
+| Maven 依赖 | SQL Client JAR |
+| :----------------- | :----------------------|
+| `flink-json` | 内置 |
+
+如何创建一张基于 JSON Format 的表
+----------------
+
+以下是一个利用 Kafka 以及 JSON Format 构建表的例子。
+
+
+
+
+
+
+
diff --git a/docs/dev/table/connectors/formats/orc.md b/docs/dev/table/connectors/formats/orc.md
new file mode 100644
index 00000000000000..4c878a43467194
--- /dev/null
+++ b/docs/dev/table/connectors/formats/orc.md
@@ -0,0 +1,183 @@
+---
+title: "Orc Format"
+nav-title: Orc
+nav-parent_id: sql-formats
+nav-pos: 6
+---
+
+
+Format: Serialization Schema
+Format: Deserialization Schema
+
+* This will be replaced by the TOC
+{:toc}
+
+The [Apache Orc](https://orc.apache.org/) format allows to read and write Orc data.
+
+Dependencies
+------------
+
+In order to setup the Orc format, the following table provides dependency information for both
+projects using a build automation tool (such as Maven or SBT) and SQL Client with SQL JAR bundles.
+
+| Maven dependency | SQL Client JAR |
+| :----------------- | :----------------------|
+| flink-orc{{site.scala_version_suffix}} |{% if site.is_stable %}[Download](https://repo.maven.apache.org/maven2/org/apache/flink/flink-sql-orc{{site.scala_version_suffix}}/{{site.version}}/flink-sql-orc{{site.scala_version_suffix}}-{{site.version}}.jar) {% else %} Only available for stable releases {% endif %}|
+
+How to create a table with Orc format
+----------------
+
+Here is an example to create a table using Filesystem connector and Orc format.
+
+
+
+Orc format also supports table properties from [Table properties](https://orc.apache.org/docs/hive-config.html#table-properties).
+For example, you can configure `orc.compress=SNAPPY` to enable snappy compression.
+
+Data Type Mapping
+----------------
+
+Orc format type mapping is compatible with Apache Hive.
+The following table lists the type mapping from Flink type to Orc type.
+
+
+
+
+
Flink Data Type
+
Orc physical type
+
Orc logical type
+
+
+
+
+
CHAR
+
bytes
+
CHAR
+
+
+
VARCHAR
+
bytes
+
VARCHAR
+
+
+
STRING
+
bytes
+
STRING
+
+
+
BOOLEAN
+
long
+
BOOLEAN
+
+
+
BYTES
+
bytes
+
BINARY
+
+
+
DECIMAL
+
decimal
+
DECIMAL
+
+
+
TINYINT
+
long
+
BYTE
+
+
+
SMALLINT
+
long
+
SHORT
+
+
+
INT
+
long
+
INT
+
+
+
BIGINT
+
long
+
LONG
+
+
+
FLOAT
+
double
+
FLOAT
+
+
+
DOUBLE
+
double
+
DOUBLE
+
+
+
DATE
+
long
+
DATE
+
+
+
TIMESTAMP
+
timestamp
+
TIMESTAMP
+
+
+
+
+Attention Composite data type: Array, Map and Row are not supported.
diff --git a/docs/dev/table/connectors/formats/orc.zh.md b/docs/dev/table/connectors/formats/orc.zh.md
new file mode 100644
index 00000000000000..03b73f8822eb71
--- /dev/null
+++ b/docs/dev/table/connectors/formats/orc.zh.md
@@ -0,0 +1,180 @@
+---
+title: "Orc Format"
+nav-title: Orc
+nav-parent_id: sql-formats
+nav-pos: 6
+---
+
+
+Format: Serialization Schema
+Format: Deserialization Schema
+
+* This will be replaced by the TOC {:toc}
+{:toc}
+
+[Apache Orc](https://orc.apache.org/) Format 允许读写 ORC 数据。
+
+依赖
+------------
+
+为了建立Orc格式,下列的表格提供了为项目使用自动化工具(例如Maven或者SBT)以及SQL客户端使用SQL JAR包的依赖信息。
+
+| Maven 依赖 | SQL 客户端 JAR |
+| :----------------- | :----------------------|
+| flink-orc{{site.scala_version_suffix}} |{% if site.is_stable %}[Download](https://repo.maven.apache.org/maven2/org/apache/flink/flink-sql-orc{{site.scala_version_suffix}}/{{site.version}}/flink-sql-orc{{site.scala_version_suffix}}-{{site.version}}.jar) {% else %} Only available for stable releases {% endif %}|
+
+
+如何用 Orc 格式创建一个表格
+----------------
+
+下面是一个用 Filesystem connector 和 Orc format 创建表格的例子
+
+
+
+注意 复合数据类型: 数组、 映射和行类型暂不支持。
diff --git a/docs/dev/table/connectors/formats/parquet.md b/docs/dev/table/connectors/formats/parquet.md
new file mode 100644
index 00000000000000..c6f94cde8f0a43
--- /dev/null
+++ b/docs/dev/table/connectors/formats/parquet.md
@@ -0,0 +1,189 @@
+---
+title: "Parquet Format"
+nav-title: Parquet
+nav-parent_id: sql-formats
+nav-pos: 5
+---
+
+
+Format: Serialization Schema
+Format: Deserialization Schema
+
+* This will be replaced by the TOC
+{:toc}
+
+The [Apache Parquet](https://parquet.apache.org/) format allows to read and write Parquet data.
+
+Dependencies
+------------
+
+In order to setup the Parquet format, the following table provides dependency information for both
+projects using a build automation tool (such as Maven or SBT) and SQL Client with SQL JAR bundles.
+
+| Maven dependency | SQL Client JAR |
+| :----------------- | :----------------------|
+| flink-parquet{{site.scala_version_suffix}} |{% if site.is_stable %}[Download](https://repo.maven.apache.org/maven2/org/apache/flink/flink-sql-parquet{{site.scala_version_suffix}}/{{site.version}}/flink-sql-parquet{{site.scala_version_suffix}}-{{site.version}}.jar) {% else %} Only available for stable releases {% endif %}|
+
+How to create a table with Parquet format
+----------------
+
+Here is an example to create a table using Filesystem connector and Parquet format.
+
+
Specify what format to use, here should be 'parquet'.
+
+
+
parquet.utc-timezone
+
optional
+
false
+
Boolean
+
Use UTC timezone or local timezone to the conversion between epoch time and LocalDateTime. Hive 0.x/1.x/2.x use local timezone. But Hive 3.x use UTC timezone.
+
+
+
+
+Parquet format also supports configuration from [ParquetOutputFormat](https://www.javadoc.io/doc/org.apache.parquet/parquet-hadoop/1.10.0/org/apache/parquet/hadoop/ParquetOutputFormat.html).
+For example, you can configure `parquet.compression=GZIP` to enable gzip compression.
+
+Data Type Mapping
+----------------
+
+Currently, Parquet format type mapping is compatible with Apache Hive, but different with Apache Spark:
+
+- Timestamp: mapping timestamp type to int96 whatever the precision is.
+- Decimal: mapping decimal type to fixed length byte array according to the precision.
+
+The following table lists the type mapping from Flink type to Parquet type.
+
+
+
+
+
Flink Data Type
+
Parquet type
+
Parquet logical type
+
+
+
+
+
CHAR / VARCHAR / STRING
+
BINARY
+
UTF8
+
+
+
BOOLEAN
+
BOOLEAN
+
+
+
+
BINARY / VARBINARY
+
BINARY
+
+
+
+
DECIMAL
+
FIXED_LEN_BYTE_ARRAY
+
DECIMAL
+
+
+
TINYINT
+
INT32
+
INT_8
+
+
+
SMALLINT
+
INT32
+
INT_16
+
+
+
INT
+
INT32
+
+
+
+
BIGINT
+
INT64
+
+
+
+
FLOAT
+
FLOAT
+
+
+
+
DOUBLE
+
DOUBLE
+
+
+
+
DATE
+
INT32
+
DATE
+
+
+
TIME
+
INT32
+
TIME_MILLIS
+
+
+
TIMESTAMP
+
INT96
+
+
+
+
+
+Attention Composite data type: Array, Map and Row are not supported.
diff --git a/docs/dev/table/connectors/formats/parquet.zh.md b/docs/dev/table/connectors/formats/parquet.zh.md
new file mode 100644
index 00000000000000..e6a4876ca7ed59
--- /dev/null
+++ b/docs/dev/table/connectors/formats/parquet.zh.md
@@ -0,0 +1,189 @@
+---
+title: "Parquet Format"
+nav-title: Parquet
+nav-parent_id: sql-formats
+nav-pos: 5
+---
+
+
+Format: Serialization Schema
+Format: Deserialization Schema
+
+* This will be replaced by the TOC
+{:toc}
+
+The [Apache Parquet](https://parquet.apache.org/) format allows to read and write Parquet data.
+
+Dependencies
+------------
+
+In order to setup the Parquet format, the following table provides dependency information for both
+projects using a build automation tool (such as Maven or SBT) and SQL Client with SQL JAR bundles.
+
+| Maven dependency | SQL Client JAR |
+| :----------------- | :----------------------|
+| flink-parquet{{site.scala_version_suffix}} |{% if site.is_stable %}[Download](https://repo.maven.apache.org/maven2/org/apache/flink/flink-sql-parquet{{site.scala_version_suffix}}/{{site.version}}/flink-sql-parquet{{site.scala_version_suffix}}-{{site.version}}.jar) {% else %} Only available for stable releases {% endif %}|
+
+How to create a table with Parquet format
+----------------
+
+Here is an example to create a table using Filesystem connector and Parquet format.
+
+
Specify what format to use, here should be 'parquet'.
+
+
+
parquet.utc-timezone
+
optional
+
false
+
Boolean
+
Use UTC timezone or local timezone to the conversion between epoch time and LocalDateTime. Hive 0.x/1.x/2.x use local timezone. But Hive 3.x use UTC timezone.
+
+
+
+
+Parquet format also supports configuration from [ParquetOutputFormat](https://www.javadoc.io/doc/org.apache.parquet/parquet-hadoop/1.10.0/org/apache/parquet/hadoop/ParquetOutputFormat.html).
+For example, you can configure `parquet.compression=GZIP` to enable gzip compression.
+
+Data Type Mapping
+----------------
+
+Currently, Parquet format type mapping is compatible with Apache Hive, but different with Apache Spark:
+
+- Timestamp: mapping timestamp type to int96 whatever the precision is.
+- Decimal: mapping decimal type to fixed length byte array according to the precision.
+
+The following table lists the type mapping from Flink type to Parquet type.
+
+
+
+
+
Flink Data Type
+
Parquet type
+
Parquet logical type
+
+
+
+
+
CHAR / VARCHAR / STRING
+
BINARY
+
UTF8
+
+
+
BOOLEAN
+
BOOLEAN
+
+
+
+
BINARY / VARBINARY
+
BINARY
+
+
+
+
DECIMAL
+
FIXED_LEN_BYTE_ARRAY
+
DECIMAL
+
+
+
TINYINT
+
INT32
+
INT_8
+
+
+
SMALLINT
+
INT32
+
INT_16
+
+
+
INT
+
INT32
+
+
+
+
BIGINT
+
INT64
+
+
+
+
FLOAT
+
FLOAT
+
+
+
+
DOUBLE
+
DOUBLE
+
+
+
+
DATE
+
INT32
+
DATE
+
+
+
TIME
+
INT32
+
TIME_MILLIS
+
+
+
TIMESTAMP
+
INT96
+
+
+
+
+
+Attention Composite data type: Array, Map and Row are not supported.
diff --git a/docs/dev/table/connectors/hbase.md b/docs/dev/table/connectors/hbase.md
new file mode 100644
index 00000000000000..2fab3ebf9baff1
--- /dev/null
+++ b/docs/dev/table/connectors/hbase.md
@@ -0,0 +1,303 @@
+---
+title: "HBase SQL Connector"
+nav-title: HBase
+nav-parent_id: sql-connectors
+nav-pos: 6
+---
+
+
+Scan Source: Bounded
+Lookup Source: Sync Mode
+Sink: Batch
+Sink: Streaming Upsert Mode
+
+* This will be replaced by the TOC
+{:toc}
+
+The HBase connector allows for reading from and writing to an HBase cluster. This document describes how to setup the HBase Connector to run SQL queries against HBase.
+
+HBase always works in upsert mode for exchange changelog messages with the external system using a primary key defined on the DDL. The primary key must be defined on the HBase rowkey field (rowkey field must be declared). If the PRIMARY KEY clause is not declared, the HBase connector will take rowkey as the primary key by default.
+
+Dependencies
+------------
+
+In order to setup the HBase connector, the following table provide dependency information for both projects using a build automation tool (such as Maven or SBT) and SQL Client with SQL JAR bundles.
+
+| HBase Version | Maven dependency | SQL Client JAR |
+| :------------------ | :-------------------------------------------------------- | :----------------------|
+| 1.4.x | `flink-connector-hbase{{site.scala_version_suffix}}` | Unsupported |
+
+*Note: To use HBase connector in SQL Client or Flink cluster, it's highly recommended to add HBase dependency jars to Hadoop classpath. Flink will load all jars under Hadoop classpath automatically, please refer to [HBase, MapReduce, and the CLASSPATH](https://hbase.apache.org/book.html#hbase.mapreduce.classpath) about how to add HBase dependency jars to Hadoop classpath.*
+
+
+How to use HBase table
+----------------
+
+All the column families in HBase table must be declared as ROW type, the field name maps to the column family name, and the nested field names map to the column qualifier names. There is no need to declare all the families and qualifiers in the schema, users can declare what’s used in the query. Except the ROW type fields, the single atomic type field (e.g. STRING, BIGINT) will be recognized as HBase rowkey. The rowkey field can be arbitrary name, but should be quoted using backticks if it is a reserved keyword.
+
+
+
+{% highlight sql %}
+-- register the HBase table 'mytable' in Flink SQL
+CREATE TABLE hTable (
+ rowkey INT,
+ family1 ROW,
+ family2 ROW,
+ family3 ROW,
+ PRIMARY KEY (rowkey) NOT ENFORCED
+) WITH (
+ 'connector' = 'hbase-1.4',
+ 'table-name' = 'mytable',
+ 'zookeeper.quorum' = 'localhost:2181'
+);
+
+-- use ROW(...) construction function construct column families and write data into the HBase table.
+-- assuming the schema of "T" is [rowkey, f1q1, f2q2, f2q3, f3q4, f3q5, f3q6]
+INSERT INTO hTable
+SELECT rowkey, ROW(f1q1), ROW(f2q2, f2q3), ROW(f3q4, f3q5, f3q6) FROM T;
+
+-- scan data from the HBase table
+SELECT rowkey, family1, family3.q4, family3.q6 FROM hTable;
+
+-- temporal join the HBase table as a dimension table
+SELECT * FROM myTopic
+LEFT JOIN hTable FOR SYSTEM_TIME AS OF myTopic.proctime
+ON myTopic.key = hTable.rowkey;
+{% endhighlight %}
+
+
+
+Connector Options
+----------------
+
+
+
+
+
Option
+
Required
+
Default
+
Type
+
Description
+
+
+
+
+
connector
+
required
+
(none)
+
String
+
Specify what connector to use, here should be 'hbase-1.4'.
+
+
+
table-name
+
required
+
(none)
+
String
+
The name of HBase table to connect.
+
+
+
zookeeper.quorum
+
required
+
(none)
+
String
+
The HBase Zookeeper quorum.
+
+
+
zookeeper.znode.parent
+
optional
+
/hbase
+
String
+
The root dir in Zookeeper for HBase cluster.
+
+
+
null-string-literal
+
optional
+
null
+
String
+
Representation for null values for string fields. HBase source and sink encodes/decodes empty bytes as null values for all types except string type.
+
+
+
sink.buffer-flush.max-size
+
optional
+
2mb
+
MemorySize
+
Writing option, maximum size in memory of buffered rows for each writing request.
+ This can improve performance for writing data to HBase database, but may increase the latency.
+ Can be set to '0' to disable it.
+
+
+
+
sink.buffer-flush.max-rows
+
optional
+
1000
+
Integer
+
Writing option, maximum number of rows to buffer for each writing request.
+ This can improve performance for writing data to HBase database, but may increase the latency.
+ Can be set to '0' to disable it.
+
+
+
+
sink.buffer-flush.interval
+
optional
+
1s
+
Duration
+
Writing option, the interval to flush any buffered rows.
+ This can improve performance for writing data to HBase database, but may increase the latency.
+ Can be set to '0' to disable it. Note, both 'sink.buffer-flush.max-size' and 'sink.buffer-flush.max-rows'
+ can be set to '0' with the flush interval set allowing for complete async processing of buffered actions.
+
+
+
+
+
+
+
+Data Type Mapping
+----------------
+
+HBase stores all data as byte arrays. The data needs to be serialized and deserialized during read and write operation
+
+When serializing and de-serializing, Flink HBase connector uses utility class `org.apache.hadoop.hbase.util.Bytes` provided by HBase (Hadoop) to convert Flink Data Types to and from byte arrays.
+
+Flink HBase connector encodes `null` values to empty bytes, and decode empty bytes to `null` values for all data types except string type. For string type, the null literal is determined by `null-string-literal` option.
+
+The data type mappings are as follows:
+
+
+
+{% top %}
diff --git a/docs/dev/table/connectors/index.md b/docs/dev/table/connectors/index.md
new file mode 100644
index 00000000000000..c7fbf6fb1a11a7
--- /dev/null
+++ b/docs/dev/table/connectors/index.md
@@ -0,0 +1,268 @@
+---
+title: "Table & SQL Connectors"
+nav-id: sql-connectors
+nav-parent_id: connectors-root
+nav-pos: 2
+nav-show_overview: true
+---
+
+
+
+Flink's Table API & SQL programs can be connected to other external systems for reading and writing both batch and streaming tables. A table source provides access to data which is stored in external systems (such as a database, key-value store, message queue, or file system). A table sink emits a table to an external storage system. Depending on the type of source and sink, they support different formats such as CSV, Avro, Parquet, or ORC.
+
+This page describes how to register table sources and table sinks in Flink using the natively supported connectors. After a source or sink has been registered, it can be accessed by Table API & SQL statements.
+
+NOTE If you want to implement your own *custom* table source or sink, have a look at the [user-defined sources & sinks page]({% link dev/table/sourceSinks.md %}).
+
+Attention Flink Table & SQL introduces a new set of connector options since 1.11.0, if you are using the legacy connector options, please refer to the [legacy documentation]({% link dev/table/connect.md %}).
+
+* This will be replaced by the TOC
+{:toc}
+
+Supported Connectors
+------------
+
+Flink natively support various connectors. The following tables list all available connectors.
+
+
+
+{% top %}
+
+How to use connectors
+--------
+
+Flink supports to use SQL CREATE TABLE statement to register a table. One can define the table name, the table schema, and the table options for connecting to an external system.
+
+The following code shows a full example of how to connect to Kafka for reading Json records.
+
+
+
+{% highlight sql %}
+CREATE TABLE MyUserTable (
+ -- declare the schema of the table
+ `user` BIGINT,
+ message STRING,
+ ts TIMESTAMP,
+ proctime AS PROCTIME(), -- use computed column to define proctime attribute
+ WATERMARK FOR ts AS ts - INTERVAL '5' SECOND -- use WATERMARK statement to define rowtime attribute
+) WITH (
+ -- declare the external system to connect to
+ 'connector' = 'kafka',
+ 'topic' = 'topic_name',
+ 'scan.startup.mode' = 'earliest-offset',
+ 'properties.bootstrap.servers' = 'localhost:9092',
+ 'format' = 'json' -- declare a format for this system
+)
+{% endhighlight %}
+
+
+
+In this way the desired connection properties are converted into string-based key-value pairs. So-called [table factories]({% link dev/table/sourceSinks.md %}#define-a-tablefactory) create configured table sources, table sinks, and corresponding formats from the key-value pairs. All table factories that can be found via Java's [Service Provider Interfaces (SPI)](https://docs.oracle.com/javase/tutorial/sound/SPI-intro.html) are taken into account when searching for exactly-one matching table factory.
+
+If no factory can be found or multiple factories match for the given properties, an exception will be thrown with additional information about considered factories and supported properties.
+
+{% top %}
+
+Schema Mapping
+------------
+
+The body clause of a SQL `CREATE TABLE` statement defines the names and types of columns, constraints and watermarks. Flink doesn't hold the data, thus the schema definition only declares how to map types from an external system to Flink’s representation. The mapping may not be mapped by names, it depends on the implementation of formats and connectors. For example, a MySQL database table is mapped by field names (not case sensitive), and a CSV filesystem is mapped by field order (field names can be arbitrary). This will be explained in every connectors.
+
+The following example shows a simple schema without time attributes and one-to-one field mapping of input/output to table columns.
+
+
+
+### Primary Key
+
+Primary key constraints tell that a column or a set of columns of a table are unique and they do not contain nulls. Primary key uniquely identifies a row in a table.
+
+The primary key of a source table is a metadata information for optimization. The primary key of a sink table is usually used by the sink implementation for upserting.
+
+SQL standard specifies that a constraint can either be ENFORCED or NOT ENFORCED. This controls if the constraint checks are performed on the incoming/outgoing data. Flink does not own the data the only mode we want to support is the NOT ENFORCED mode. Its up to the user to ensure that the query enforces key integrity.
+
+
+
+### Time Attributes
+
+Time attributes are essential when working with unbounded streaming tables. Therefore both proctime and rowtime attributes can be defined as part of the schema.
+
+For more information about time handling in Flink and especially event-time, we recommend the general [event-time section]({% link dev/table/streaming/time_attributes.md %}).
+
+#### Proctime Attributes
+
+In order to declare a proctime attribute in the schema, you can use [Computed Column syntax]({% link dev/table/sql/create.md %}#create-table) to declare a computed column which is generated from `PROCTIME()` builtin function.
+The computed column is a virtual column which is not stored in the physical data.
+
+
+
+#### Rowtime Attributes
+
+In order to control the event-time behavior for tables, Flink provides predefined timestamp extractors and watermark strategies.
+
+Please refer to [CREATE TABLE statements]({% link dev/table/sql/create.md %}#create-table) for more information about defining time attributes in DDL.
+
+The following timestamp extractors are supported:
+
+
+
+{% highlight sql %}
+-- use the existing TIMESTAMP(3) field in schema as the rowtime attribute
+CREATE TABLE MyTable (
+ ts_field TIMESTAMP(3),
+ WATERMARK FOR ts_field AS ...
+) WITH (
+ ...
+)
+
+-- use system functions or UDFs or expressions to extract the expected TIMESTAMP(3) rowtime field
+CREATE TABLE MyTable (
+ log_ts STRING,
+ ts_field AS TO_TIMESTAMP(log_ts),
+ WATERMARK FOR ts_field AS ...
+) WITH (
+ ...
+)
+{% endhighlight %}
+
+
+
+The following watermark strategies are supported:
+
+
+
+{% highlight sql %}
+-- Sets a watermark strategy for strictly ascending rowtime attributes. Emits a watermark of the
+-- maximum observed timestamp so far. Rows that have a timestamp smaller to the max timestamp
+-- are not late.
+CREATE TABLE MyTable (
+ ts_field TIMESTAMP(3),
+ WATERMARK FOR ts_field AS ts_field
+) WITH (
+ ...
+)
+
+-- Sets a watermark strategy for ascending rowtime attributes. Emits a watermark of the maximum
+-- observed timestamp so far minus 1. Rows that have a timestamp equal to the max timestamp
+-- are not late.
+CREATE TABLE MyTable (
+ ts_field TIMESTAMP(3),
+ WATERMARK FOR ts_field AS ts_field - INTERVAL '0.001' SECOND
+) WITH (
+ ...
+)
+
+-- Sets a watermark strategy for rowtime attributes which are out-of-order by a bounded time interval.
+-- Emits watermarks which are the maximum observed timestamp minus the specified delay, e.g. 2 seconds.
+CREATE TABLE MyTable (
+ ts_field TIMESTAMP(3),
+ WATERMARK FOR ts_field AS ts_field - INTERVAL '2' SECOND
+) WITH (
+ ...
+)
+{% endhighlight %}
+
+
+
+Make sure to always declare both timestamps and watermarks. Watermarks are required for triggering time-based operations.
+
+### SQL Types
+
+Please see the [Data Types]({% link dev/table/types.md %}) page about how to declare a type in SQL.
+
+{% top %}
\ No newline at end of file
diff --git a/docs/dev/table/connectors/index.zh.md b/docs/dev/table/connectors/index.zh.md
new file mode 100644
index 00000000000000..6e5ece58cdefe1
--- /dev/null
+++ b/docs/dev/table/connectors/index.zh.md
@@ -0,0 +1,268 @@
+---
+title: "Table & SQL Connectors"
+nav-id: sql-connectors
+nav-parent_id: connectors-root
+nav-pos: 2
+nav-show_overview: true
+---
+
+
+
+Flink's Table API & SQL programs can be connected to other external systems for reading and writing both batch and streaming tables. A table source provides access to data which is stored in external systems (such as a database, key-value store, message queue, or file system). A table sink emits a table to an external storage system. Depending on the type of source and sink, they support different formats such as CSV, Avro, Parquet, or ORC.
+
+This page describes how to register table sources and table sinks in Flink using the natively supported connectors. After a source or sink has been registered, it can be accessed by Table API & SQL statements.
+
+NOTE If you want to implement your own *custom* table source or sink, have a look at the [user-defined sources & sinks page]({% link dev/table/sourceSinks.zh.md %}).
+
+Attention Flink Table & SQL introduces a new set of connector options since 1.11.0, if you are using the legacy connector options, please refer to the [legacy documentation]({% link dev/table/connect.zh.md %}).
+
+* This will be replaced by the TOC
+{:toc}
+
+Supported Connectors
+------------
+
+Flink natively support various connectors. The following tables list all available connectors.
+
+
+
+{% top %}
+
+How to use connectors
+--------
+
+Flink supports to use SQL CREATE TABLE statement to register a table. One can define the table name, the table schema, and the table options for connecting to an external system.
+
+The following code shows a full example of how to connect to Kafka for reading Json records.
+
+
+
+{% highlight sql %}
+CREATE TABLE MyUserTable (
+ -- declare the schema of the table
+ `user` BIGINT,
+ message STRING,
+ ts TIMESTAMP,
+ proctime AS PROCTIME(), -- use computed column to define proctime attribute
+ WATERMARK FOR ts AS ts - INTERVAL '5' SECOND -- use WATERMARK statement to define rowtime attribute
+) WITH (
+ -- declare the external system to connect to
+ 'connector' = 'kafka',
+ 'topic' = 'topic_name',
+ 'scan.startup.mode' = 'earliest-offset',
+ 'properties.bootstrap.servers' = 'localhost:9092',
+ 'format' = 'json' -- declare a format for this system
+)
+{% endhighlight %}
+
+
+
+In this way the desired connection properties are converted into string-based key-value pairs. So-called [table factories]({% link dev/table/sourceSinks.zh.md %}#define-a-tablefactory) create configured table sources, table sinks, and corresponding formats from the key-value pairs. All table factories that can be found via Java's [Service Provider Interfaces (SPI)](https://docs.oracle.com/javase/tutorial/sound/SPI-intro.html) are taken into account when searching for exactly-one matching table factory.
+
+If no factory can be found or multiple factories match for the given properties, an exception will be thrown with additional information about considered factories and supported properties.
+
+{% top %}
+
+Schema Mapping
+------------
+
+The body clause of a SQL `CREATE TABLE` statement defines the names and types of columns, constraints and watermarks. Flink doesn't hold the data, thus the schema definition only declares how to map types from an external system to Flink’s representation. The mapping may not be mapped by names, it depends on the implementation of formats and connectors. For example, a MySQL database table is mapped by field names (not case sensitive), and a CSV filesystem is mapped by field order (field names can be arbitrary). This will be explained in every connectors.
+
+The following example shows a simple schema without time attributes and one-to-one field mapping of input/output to table columns.
+
+
+
+### Primary Key
+
+Primary key constraints tell that a column or a set of columns of a table are unique and they do not contain nulls. Primary key uniquely identifies a row in a table.
+
+The primary key of a source table is a metadata information for optimization. The primary key of a sink table is usually used by the sink implementation for upserting.
+
+SQL standard specifies that a constraint can either be ENFORCED or NOT ENFORCED. This controls if the constraint checks are performed on the incoming/outgoing data. Flink does not own the data the only mode we want to support is the NOT ENFORCED mode. Its up to the user to ensure that the query enforces key integrity.
+
+
+
+### Time Attributes
+
+Time attributes are essential when working with unbounded streaming tables. Therefore both proctime and rowtime attributes can be defined as part of the schema.
+
+For more information about time handling in Flink and especially event-time, we recommend the general [event-time section]({% link dev/table/streaming/time_attributes.zh.md %}).
+
+#### Proctime Attributes
+
+In order to declare a proctime attribute in the schema, you can use [Computed Column syntax]({% link dev/table/sql/create.zh.md %}#create-table) to declare a computed column which is generated from `PROCTIME()` builtin function.
+The computed column is a virtual column which is not stored in the physical data.
+
+
+
+#### Rowtime Attributes
+
+In order to control the event-time behavior for tables, Flink provides predefined timestamp extractors and watermark strategies.
+
+Please refer to [CREATE TABLE statements]({% link dev/table/sql/create.zh.md %}#create-table) for more information about defining time attributes in DDL.
+
+The following timestamp extractors are supported:
+
+
+
+{% highlight sql %}
+-- use the existing TIMESTAMP(3) field in schema as the rowtime attribute
+CREATE TABLE MyTable (
+ ts_field TIMESTAMP(3),
+ WATERMARK FOR ts_field AS ...
+) WITH (
+ ...
+)
+
+-- use system functions or UDFs or expressions to extract the expected TIMESTAMP(3) rowtime field
+CREATE TABLE MyTable (
+ log_ts STRING,
+ ts_field AS TO_TIMESTAMP(log_ts),
+ WATERMARK FOR ts_field AS ...
+) WITH (
+ ...
+)
+{% endhighlight %}
+
+
+
+The following watermark strategies are supported:
+
+
+
+{% highlight sql %}
+-- Sets a watermark strategy for strictly ascending rowtime attributes. Emits a watermark of the
+-- maximum observed timestamp so far. Rows that have a timestamp smaller to the max timestamp
+-- are not late.
+CREATE TABLE MyTable (
+ ts_field TIMESTAMP(3),
+ WATERMARK FOR ts_field AS ts_field
+) WITH (
+ ...
+)
+
+-- Sets a watermark strategy for ascending rowtime attributes. Emits a watermark of the maximum
+-- observed timestamp so far minus 1. Rows that have a timestamp equal to the max timestamp
+-- are not late.
+CREATE TABLE MyTable (
+ ts_field TIMESTAMP(3),
+ WATERMARK FOR ts_field AS ts_field - INTERVAL '0.001' SECOND
+) WITH (
+ ...
+)
+
+-- Sets a watermark strategy for rowtime attributes which are out-of-order by a bounded time interval.
+-- Emits watermarks which are the maximum observed timestamp minus the specified delay, e.g. 2 seconds.
+CREATE TABLE MyTable (
+ ts_field TIMESTAMP(3),
+ WATERMARK FOR ts_field AS ts_field - INTERVAL '2' SECOND
+) WITH (
+ ...
+)
+{% endhighlight %}
+
+
+
+Make sure to always declare both timestamps and watermarks. Watermarks are required for triggering time-based operations.
+
+### SQL Types
+
+Please see the [Data Types]({% link dev/table/types.zh.md %}) page about how to declare a type in SQL.
+
+{% top %}
diff --git a/docs/dev/table/connectors/jdbc.md b/docs/dev/table/connectors/jdbc.md
new file mode 100644
index 00000000000000..addf1c834e2236
--- /dev/null
+++ b/docs/dev/table/connectors/jdbc.md
@@ -0,0 +1,562 @@
+---
+title: "JDBC SQL Connector"
+nav-title: JDBC
+nav-parent_id: sql-connectors
+nav-pos: 3
+---
+
+
+Scan Source: Bounded
+Lookup Source: Sync Mode
+Sink: Batch
+Sink: Streaming Append & Upsert Mode
+
+* This will be replaced by the TOC
+{:toc}
+
+The JDBC connector allows for reading data from and writing data into any relational databases with a JDBC driver. This document describes how to setup the JDBC connector to run SQL queries against relational databases.
+
+The JDBC sink operate in upsert mode for exchange UPDATE/DELETE messages with the external system if a primary key is defined on the DDL, otherwise, it operates in append mode and doesn't support to consume UPDATE/DELETE messages.
+
+Dependencies
+------------
+
+In order to setup the JDBC connector, the following table provides dependency information for both projects using a build automation tool (such as Maven or SBT) and SQL Client with SQL JAR bundles.
+
+| Maven dependency | SQL Client JAR |
+| :------------------------------------------------- | :-------------------------------------------------------- |
+| `flink-connector-jdbc{{site.scala_version_suffix}}`| {% if site.is_stable %} [Download](https://repo.maven.apache.org/maven2/org/apache/flink/flink-connector-jdbc{{site.scala_version_suffix}}/{{site.version}}/flink-connector-jdbc{{site.scala_version_suffix}}-{{site.version}}.jar) {% else %} Only available for [stable releases]({{ site.stable_baseurl }}/dev/table/connectors/jdbc.html) {% endif %}|
+
+
+A driver dependency is also required to connect to a specified database. Here are drivers currently supported:
+
+| Driver | Group Id | Artifact Id | JAR |
+| :-----------| :------------------| :----------------------| :----------------|
+| MySQL | `mysql` | `mysql-connector-java` | [Download](https://repo.maven.apache.org/maven2/mysql/mysql-connector-java/) |
+| PostgreSQL | `org.postgresql` | `postgresql` | [Download](https://jdbc.postgresql.org/download.html) |
+| Derby | `org.apache.derby` | `derby` | [Download](http://db.apache.org/derby/derby_downloads.html) |
+
+
+JDBC connector and drivers are not currently part of Flink's binary distribution. See how to link with them for cluster execution [here]({% link dev/project-configuration.md %}).
+
+
+How to create a JDBC table
+----------------
+
+The JDBC table can be defined as following:
+
+
+
+{% highlight sql %}
+-- register a MySQL table 'users' in Flink SQL
+CREATE TABLE MyUserTable (
+ id BIGINT,
+ name STRING,
+ age INT,
+ status BOOLEAN,
+ PRIMARY KEY (id) NOT ENFORCED
+) WITH (
+ 'connector' = 'jdbc',
+ 'url' = 'jdbc:mysql://localhost:3306/mydatabase',
+ 'table-name' = 'users'
+);
+
+-- write data into the JDBC table from the other table "T"
+INSERT INTO MyUserTable
+SELECT id, name, age, status FROM T;
+
+-- scan data from the JDBC table
+SELECT id, name, age, status FROM MyUserTable;
+
+-- temporal join the JDBC table as a dimension table
+SELECT * FROM myTopic
+LEFT JOIN MyUserTable FOR SYSTEM_TIME AS OF myTopic.proctime
+ON myTopic.key = MyUserTable.id;
+{% endhighlight %}
+
+
+
+Connector Options
+----------------
+
+
+
+
+
Option
+
Required
+
Default
+
Type
+
Description
+
+
+
+
+
connector
+
required
+
(none)
+
String
+
Specify what connector to use, here should be 'jdbc'.
+
+
+
url
+
required
+
(none)
+
String
+
The JDBC database url.
+
+
+
table-name
+
required
+
(none)
+
String
+
The name of JDBC table to connect.
+
+
+
driver
+
optional
+
(none)
+
String
+
The class name of the JDBC driver to use to connect to this URL, if not set, it will automatically be derived from the URL.
+
+
+
username
+
optional
+
(none)
+
String
+
The JDBC user name. 'username' and 'password' must both be specified if any of them is specified.
+
+
+
password
+
optional
+
(none)
+
String
+
The JDBC password.
+
+
+
scan.partition.column
+
optional
+
(none)
+
String
+
The column name used for partitioning the input. See the following Partitioned Scan section for more details.
+
+
+
scan.partition.num
+
optional
+
(none)
+
Integer
+
The number of partitions.
+
+
+
scan.partition.lower-bound
+
optional
+
(none)
+
Integer
+
The smallest value of the first partition.
+
+
+
scan.partition.upper-bound
+
optional
+
(none)
+
Integer
+
The largest value of the last partition.
+
+
+
scan.fetch-size
+
optional
+
0
+
Integer
+
The number of rows that should be fetched from the database when reading per round trip. If the value specified is zero, then the hint is ignored.
+
+
+
lookup.cache.max-rows
+
optional
+
(none)
+
Integer
+
The max number of rows of lookup cache, over this value, the oldest rows will be expired.
+ Lookup cache is disabled by default. See the following Lookup Cache section for more details.
+
+
+
lookup.cache.ttl
+
optional
+
(none)
+
Integer
+
The max time to live for each rows in lookup cache, over this time, the oldest rows will be expired.
+ Lookup cache is disabled by default. See the following Lookup Cache section for more details.
+
+
+
lookup.max-retries
+
optional
+
3
+
Integer
+
The max retry times if lookup database failed.
+
+
+
sink.buffer-flush.max-rows
+
optional
+
100
+
Integer
+
The max size of buffered records before flush. Can be set to zero to disable it.
+
+
+
sink.buffer-flush.interval
+
optional
+
1s
+
Duration
+
The flush interval mills, over this time, asynchronous threads will flush data. Can be set to '0' to disable it. Note, 'sink.buffer-flush.max-rows' can be set to '0' with the flush interval set allowing for complete async processing of buffered actions.
+
+
+
sink.max-retries
+
optional
+
3
+
Integer
+
The max retry times if writing records to database failed.
+
+
+
+
+Features
+--------
+
+### Key handling
+
+Flink uses the primary key that defined in DDL when writing data to external databases. The connector operate in upsert mode if the primary key was defined, otherwise, the connector operate in append mode.
+
+In upsert mode, Flink will insert a new row or update the existing row according to the primary key, Flink can ensure the idempotence in this way. To guarantee the output result is as expected, it's recommended to define primary key for the table and make sure the primary key is one of the unique key sets or primary key of the underlying database table. In append mode, Flink will interpret all records as INSERT messages, the INSERT operation may fail if a primary key or unique constraint violation happens in the underlying database.
+
+See [CREATE TABLE DDL]({% link dev/table/sql/create.md %}#create-table) for more details about PRIMARY KEY syntax.
+
+### Partitioned Scan
+
+To accelerate reading data in parallel `Source` task instances, Flink provides partitioned scan feature for JDBC table.
+
+All the following scan partition options must all be specified if any of them is specified. They describe how to partition the table when reading in parallel from multiple tasks.
+The `scan.partition.column` must be a numeric, date, or timestamp column from the table in question. Notice that `scan.partition.lower-bound` and `scan.partition.upper-bound` are just used to decide the partition stride, not for filtering the rows in table. So all rows in the table will be partitioned and returned.
+
+- `scan.partition.column`: The column name used for partitioning the input.
+- `scan.partition.num`: The number of partitions.
+- `scan.partition.lower-bound`: The smallest value of the first partition.
+- `scan.partition.upper-bound`: The largest value of the last partition.
+
+### Lookup Cache
+
+JDBC connector can be used in temporal join as a lookup source (aka. dimension table). Currently, only sync lookup mode is supported.
+
+By default, lookup cache is not enabled. You can enable it by setting both `lookup.cache.max-rows` and `lookup.cache.ttl`.
+
+The lookup cache is used to improve performance of temporal join the JDBC connector. By default, lookup cache is not enabled, so all the requests are sent to external database.
+When lookup cache is enabled, each process (i.e. TaskManager) will hold a cache. Flink will lookup the cache first, and only send requests to external database when cache missing, and update cache with the rows returned.
+The oldest rows in cache will be expired when the cache hit to the max cached rows `lookup.cache.max-rows` or when the row exceeds the max time to live `lookup.cache.ttl`.
+The cached rows might not be the latest, users can tune `lookup.cache.ttl` to a smaller value to have a better fresh data, but this may increase the number of requests send to database. So this is a balance between throughput and correctness.
+
+### Idempotent Writes
+
+JDBC sink will use upsert semantics rather than plain INSERT statements if primary key is defined in DDL. Upsert semantics refer to atomically adding a new row or updating the existing row if there is a unique constraint violation in the underlying database, which provides idempotence.
+
+If there are failures, the Flink job will recover and re-process from last successful checkpoint, which can lead to re-processing messages during recovery. The upsert mode is highly recommended as it helps avoid constraint violations or duplicate data if records need to be re-processed.
+
+Aside from failure recovery, the source topic may also naturally contain multiple records over time with the same primary key, making upserts desirable.
+
+As there is no standard syntax for upsert, the following table describes the database-specific DML that is used.
+
+
+
+
+
Database
+
Upsert Grammar
+
+
+
+
+
MySQL
+
INSERT .. ON DUPLICATE KEY UPDATE ..
+
+
+
PostgreSQL
+
INSERT .. ON CONFLICT .. DO UPDATE SET ..
+
+
+
+
+### Postgres Database as a Catalog
+
+The `JdbcCatalog` enables users to connect Flink to relational databases over JDBC protocol.
+
+Currently, `PostgresCatalog` is the only implementation of JDBC Catalog at the moment, `PostgresCatalog` only supports limited `Catalog` methods include:
+
+{% highlight java %}
+// The supported methods by Postgres Catalog.
+PostgresCatalog.databaseExists(String databaseName)
+PostgresCatalog.listDatabases()
+PostgresCatalog.getDatabase(String databaseName)
+PostgresCatalog.listTables(String databaseName)
+PostgresCatalog.getTable(ObjectPath tablePath)
+PostgresCatalog.tableExists(ObjectPath tablePath)
+{% endhighlight %}
+
+Other `Catalog` methods is unsupported now.
+
+#### Usage of PostgresCatalog
+
+Please refer to [Dependencies](#dependencies) section for how to setup a JDBC connector and Postgres driver.
+
+Postgres catalog supports the following options:
+- `name`: required, name of the catalog.
+- `default-database`: required, default database to connect to.
+- `username`: required, username of Postgres account.
+- `password`: required, password of the account.
+- `base-url`: required, should be of format `"jdbc:postgresql://:"`, and should not contain database name here.
+
+
+{% highlight java %}
+
+EnvironmentSettings settings = EnvironmentSettings.newInstance().inStreamingMode().build();
+TableEnvironment tableEnv = TableEnvironment.create(settings);
+
+String name = "mypg";
+String defaultDatabase = "mydb";
+String username = "...";
+String password = "...";
+String baseUrl = "..."
+
+JdbcCatalog catalog = new JdbcCatalog(name, defaultDatabase, username, password, baseUrl);
+tableEnv.registerCatalog("mypg", catalog);
+
+// set the JdbcCatalog as the current catalog of the session
+tableEnv.useCatalog("mypg");
+{% endhighlight %}
+
+
+{% highlight scala %}
+
+val settings = EnvironmentSettings.newInstance().inStreamingMode().build()
+val tableEnv = TableEnvironment.create(settings)
+
+val name = "mypg"
+val defaultDatabase = "mydb"
+val username = "..."
+val password = "..."
+val baseUrl = "..."
+
+val catalog = new JdbcCatalog(name, defaultDatabase, username, password, baseUrl)
+tableEnv.registerCatalog("mypg", catalog)
+
+// set the JdbcCatalog as the current catalog of the session
+tableEnv.useCatalog("mypg")
+{% endhighlight %}
+
+
+{% highlight yaml %}
+
+execution:
+ planner: blink
+ ...
+ current-catalog: mypg # set the JdbcCatalog as the current catalog of the session
+ current-database: mydb
+
+catalogs:
+ - name: mypg
+ type: jdbc
+ default-database: mydb
+ username: ...
+ password: ...
+ base-url: ...
+{% endhighlight %}
+
+
+
+#### PostgresSQL Metaspace Mapping
+
+PostgresSQL has an additional namespace as `schema` besides database. A Postgres instance can have multiple databases, each database can have multiple schemas with a default one named "public", each schema can have multiple tables.
+In Flink, when querying tables registered by Postgres catalog, users can use either `schema_name.table_name` or just `table_name`. The `schema_name` is optional and defaults to "public".
+
+Therefor the metaspace mapping between Flink Catalog and Postgres is as following:
+
+| Flink Catalog Metaspace Structure | Postgres Metaspace Structure |
+| :------------------------------------| :-----------------------------------|
+| catalog name (defined in Flink only) | N/A |
+| database name | database name |
+| table name | [schema_name.]table_name |
+
+The full path of Postgres table in Flink should be ``"..``"`` if schema is specified, note the `` should be escaped.
+
+Here are some examples to access Postgres tables:
+
+{% highlight sql %}
+-- scan table 'test_table' of 'public' schema (i.e. the default schema), the schema name can be omitted
+SELECT * FROM mypg.mydb.test_table;
+SELECT * FROM mydb.test_table;
+SELECT * FROM test_table;
+
+-- scan table 'test_table2' of 'custom_schema' schema,
+-- the custom schema can not be omitted and must be escaped with table.
+SELECT * FROM mypg.mydb.`custom_schema.test_table2`
+SELECT * FROM mydb.`custom_schema.test_table2`;
+SELECT * FROM `custom_schema.test_table2`;
+{% endhighlight %}
+
+Data Type Mapping
+----------------
+Flink supports connect to several databases which uses dialect like MySQL, PostgresSQL, Derby. The Derby dialect usually used for testing purpose. The field data type mappings from relational databases data types to Flink SQL data types are listed in the following table, the mapping table can help define JDBC table in Flink easily.
+
+
+
+
+
MySQL type
+
PostgreSQL type
+
Flink SQL type
+
+
+
+
+
TINYINT
+
+
TINYINT
+
+
+
+ SMALLINT
+ TINYINT UNSIGNED
+
+ SMALLINT
+ INT2
+ SMALLSERIAL
+ SERIAL2
+
SMALLINT
+
+
+
+ INT
+ MEDIUMINT
+ SMALLINT UNSIGNED
+
+ INTEGER
+ SERIAL
+
INT
+
+
+
+ BIGINT
+ INT UNSIGNED
+
+ BIGINT
+ BIGSERIAL
+
BIGINT
+
+
+
BIGINT UNSIGNED
+
+
DECIMAL(20, 0)
+
+
+
BIGINT
+
BIGINT
+
BIGINT
+
+
+
FLOAT
+
+ REAL
+ FLOAT4
+
FLOAT
+
+
+
+ DOUBLE
+ DOUBLE PRECISION
+
+ FLOAT8
+ DOUBLE PRECISION
+
DOUBLE
+
+
+
+ NUMERIC(p, s)
+ DECIMAL(p, s)
+
+ NUMERIC(p, s)
+ DECIMAL(p, s)
+
DECIMAL(p, s)
+
+
+
+ BOOLEAN
+ TINYINT(1)
+
BOOLEAN
+
BOOLEAN
+
+
+
DATE
+
DATE
+
DATE
+
+
+
TIME [(p)]
+
TIME [(p)] [WITHOUT TIMEZONE]
+
TIME [(p)] [WITHOUT TIMEZONE]
+
+
+
DATETIME [(p)]
+
TIMESTAMP [(p)] [WITHOUT TIMEZONE]
+
TIMESTAMP [(p)] [WITHOUT TIMEZONE]
+
+
+
+ CHAR(n)
+ VARCHAR(n)
+ TEXT
+
+ CHAR(n)
+ CHARACTER(n)
+ VARCHAR(n)
+ CHARACTER VARYING(n)
+ TEXT
+
STRING
+
+
+
+ BINARY
+ VARBINARY
+ BLOB
+
BYTEA
+
BYTES
+
+
+
+
ARRAY
+
ARRAY
+
+
+
+
+{% top %}
\ No newline at end of file
diff --git a/docs/dev/table/connectors/jdbc.zh.md b/docs/dev/table/connectors/jdbc.zh.md
new file mode 100644
index 00000000000000..990e9b03620e47
--- /dev/null
+++ b/docs/dev/table/connectors/jdbc.zh.md
@@ -0,0 +1,562 @@
+---
+title: "JDBC SQL Connector"
+nav-title: JDBC
+nav-parent_id: sql-connectors
+nav-pos: 3
+---
+
+
+Scan Source: Bounded
+Lookup Source: Sync Mode
+Sink: Batch
+Sink: Streaming Append & Upsert Mode
+
+* This will be replaced by the TOC
+{:toc}
+
+The JDBC connector allows for reading data from and writing data into any relational databases with a JDBC driver. This document describes how to setup the JDBC connector to run SQL queries against relational databases.
+
+The JDBC sink operate in upsert mode for exchange UPDATE/DELETE messages with the external system if a primary key is defined on the DDL, otherwise, it operates in append mode and doesn't support to consume UPDATE/DELETE messages.
+
+Dependencies
+------------
+
+In order to setup the JDBC connector, the following table provides dependency information for both projects using a build automation tool (such as Maven or SBT) and SQL Client with SQL JAR bundles.
+
+| Maven dependency | SQL Client JAR |
+| :------------------------------------------------- | :-------------------------------------------------------- |
+| `flink-connector-jdbc{{site.scala_version_suffix}}`| {% if site.is_stable %} [Download](https://repo.maven.apache.org/maven2/org/apache/flink/flink-connector-jdbc{{site.scala_version_suffix}}/{{site.version}}/flink-connector-jdbc{{site.scala_version_suffix}}-{{site.version}}.jar) {% else %} Only available for [stable releases]({{ site.stable_baseurl }}/zh/dev/table/connectors/jdbc.html) {% endif %}|
+
+
+A driver dependency is also required to connect to a specified database. Here are drivers currently supported:
+
+| Driver | Group Id | Artifact Id | JAR |
+| :-----------| :------------------| :----------------------| :----------------|
+| MySQL | `mysql` | `mysql-connector-java` | [Download](https://repo.maven.apache.org/maven2/mysql/mysql-connector-java/) |
+| PostgreSQL | `org.postgresql` | `postgresql` | [Download](https://jdbc.postgresql.org/download.html) |
+| Derby | `org.apache.derby` | `derby` | [Download](http://db.apache.org/derby/derby_downloads.html) |
+
+
+JDBC connector and drivers are not currently part of Flink's binary distribution. See how to link with them for cluster execution [here]({% link dev/project-configuration.zh.md %}).
+
+
+How to create a JDBC table
+----------------
+
+The JDBC table can be defined as following:
+
+
+
+{% highlight sql %}
+-- register a MySQL table 'users' in Flink SQL
+CREATE TABLE MyUserTable (
+ id BIGINT,
+ name STRING,
+ age INT,
+ status BOOLEAN,
+ PRIMARY KEY (id) NOT ENFORCED
+) WITH (
+ 'connector' = 'jdbc',
+ 'url' = 'jdbc:mysql://localhost:3306/mydatabase',
+ 'table-name' = 'users'
+);
+
+-- write data into the JDBC table from the other table "T"
+INSERT INTO MyUserTable
+SELECT id, name, age, status FROM T;
+
+-- scan data from the JDBC table
+SELECT id, name, age, status FROM MyUserTable;
+
+-- temporal join the JDBC table as a dimension table
+SELECT * FROM myTopic
+LEFT JOIN MyUserTable FOR SYSTEM_TIME AS OF myTopic.proctime
+ON myTopic.key = MyUserTable.id;
+{% endhighlight %}
+
+
+
+Connector Options
+----------------
+
+
+
+
+
Option
+
Required
+
Default
+
Type
+
Description
+
+
+
+
+
connector
+
required
+
(none)
+
String
+
Specify what connector to use, here should be 'jdbc'.
+
+
+
url
+
required
+
(none)
+
String
+
The JDBC database url.
+
+
+
table-name
+
required
+
(none)
+
String
+
The name of JDBC table to connect.
+
+
+
driver
+
optional
+
(none)
+
String
+
The class name of the JDBC driver to use to connect to this URL, if not set, it will automatically be derived from the URL.
+
+
+
username
+
optional
+
(none)
+
String
+
The JDBC user name. 'username' and 'password' must both be specified if any of them is specified.
+
+
+
password
+
optional
+
(none)
+
String
+
The JDBC password.
+
+
+
scan.partition.column
+
optional
+
(none)
+
String
+
The column name used for partitioning the input. See the following Partitioned Scan section for more details.
+
+
+
scan.partition.num
+
optional
+
(none)
+
Integer
+
The number of partitions.
+
+
+
scan.partition.lower-bound
+
optional
+
(none)
+
Integer
+
The smallest value of the first partition.
+
+
+
scan.partition.upper-bound
+
optional
+
(none)
+
Integer
+
The largest value of the last partition.
+
+
+
scan.fetch-size
+
optional
+
0
+
Integer
+
The number of rows that should be fetched from the database when reading per round trip. If the value specified is zero, then the hint is ignored.
+
+
+
lookup.cache.max-rows
+
optional
+
(none)
+
Integer
+
The max number of rows of lookup cache, over this value, the oldest rows will be expired.
+ Lookup cache is disabled by default. See the following Lookup Cache section for more details.
+
+
+
lookup.cache.ttl
+
optional
+
(none)
+
Integer
+
The max time to live for each rows in lookup cache, over this time, the oldest rows will be expired.
+ Lookup cache is disabled by default. See the following Lookup Cache section for more details.
+
+
+
lookup.max-retries
+
optional
+
3
+
Integer
+
The max retry times if lookup database failed.
+
+
+
sink.buffer-flush.max-rows
+
optional
+
100
+
Integer
+
The max size of buffered records before flush. Can be set to zero to disable it.
+
+
+
sink.buffer-flush.interval
+
optional
+
1s
+
Duration
+
The flush interval mills, over this time, asynchronous threads will flush data. Can be set to '0' to disable it. Note, 'sink.buffer-flush.max-rows' can be set to '0' with the flush interval set allowing for complete async processing of buffered actions.
+
+
+
sink.max-retries
+
optional
+
3
+
Integer
+
The max retry times if writing records to database failed.
+
+
+
+
+Features
+--------
+
+### Key handling
+
+Flink uses the primary key that defined in DDL when writing data to external databases. The connector operate in upsert mode if the primary key was defined, otherwise, the connector operate in append mode.
+
+In upsert mode, Flink will insert a new row or update the existing row according to the primary key, Flink can ensure the idempotence in this way. To guarantee the output result is as expected, it's recommended to define primary key for the table and make sure the primary key is one of the unique key sets or primary key of the underlying database table. In append mode, Flink will interpret all records as INSERT messages, the INSERT operation may fail if a primary key or unique constraint violation happens in the underlying database.
+
+See [CREATE TABLE DDL]({% link dev/table/sql/create.zh.md %}#create-table) for more details about PRIMARY KEY syntax.
+
+### Partitioned Scan
+
+To accelerate reading data in parallel `Source` task instances, Flink provides partitioned scan feature for JDBC table.
+
+All the following scan partition options must all be specified if any of them is specified. They describe how to partition the table when reading in parallel from multiple tasks.
+The `scan.partition.column` must be a numeric, date, or timestamp column from the table in question. Notice that `scan.partition.lower-bound` and `scan.partition.upper-bound` are just used to decide the partition stride, not for filtering the rows in table. So all rows in the table will be partitioned and returned.
+
+- `scan.partition.column`: The column name used for partitioning the input.
+- `scan.partition.num`: The number of partitions.
+- `scan.partition.lower-bound`: The smallest value of the first partition.
+- `scan.partition.upper-bound`: The largest value of the last partition.
+
+### Lookup Cache
+
+JDBC connector can be used in temporal join as a lookup source (aka. dimension table). Currently, only sync lookup mode is supported.
+
+By default, lookup cache is not enabled. You can enable it by setting both `lookup.cache.max-rows` and `lookup.cache.ttl`.
+
+The lookup cache is used to improve performance of temporal join the JDBC connector. By default, lookup cache is not enabled, so all the requests are sent to external database.
+When lookup cache is enabled, each process (i.e. TaskManager) will hold a cache. Flink will lookup the cache first, and only send requests to external database when cache missing, and update cache with the rows returned.
+The oldest rows in cache will be expired when the cache hit to the max cached rows `lookup.cache.max-rows` or when the row exceeds the max time to live `lookup.cache.ttl`.
+The cached rows might not be the latest, users can tune `lookup.cache.ttl` to a smaller value to have a better fresh data, but this may increase the number of requests send to database. So this is a balance between throughput and correctness.
+
+### Idempotent Writes
+
+JDBC sink will use upsert semantics rather than plain INSERT statements if primary key is defined in DDL. Upsert semantics refer to atomically adding a new row or updating the existing row if there is a unique constraint violation in the underlying database, which provides idempotence.
+
+If there are failures, the Flink job will recover and re-process from last successful checkpoint, which can lead to re-processing messages during recovery. The upsert mode is highly recommended as it helps avoid constraint violations or duplicate data if records need to be re-processed.
+
+Aside from failure recovery, the source topic may also naturally contain multiple records over time with the same primary key, making upserts desirable.
+
+As there is no standard syntax for upsert, the following table describes the database-specific DML that is used.
+
+
+
+
+
Database
+
Upsert Grammar
+
+
+
+
+
MySQL
+
INSERT .. ON DUPLICATE KEY UPDATE ..
+
+
+
PostgreSQL
+
INSERT .. ON CONFLICT .. DO UPDATE SET ..
+
+
+
+
+### Postgres Database as a Catalog
+
+The `JdbcCatalog` enables users to connect Flink to relational databases over JDBC protocol.
+
+Currently, `PostgresCatalog` is the only implementation of JDBC Catalog at the moment, `PostgresCatalog` only supports limited `Catalog` methods include:
+
+{% highlight java %}
+// The supported methods by Postgres Catalog.
+PostgresCatalog.databaseExists(String databaseName)
+PostgresCatalog.listDatabases()
+PostgresCatalog.getDatabase(String databaseName)
+PostgresCatalog.listTables(String databaseName)
+PostgresCatalog.getTable(ObjectPath tablePath)
+PostgresCatalog.tableExists(ObjectPath tablePath)
+{% endhighlight %}
+
+Other `Catalog` methods is unsupported now.
+
+#### Usage of PostgresCatalog
+
+Please refer to [Dependencies](#dependencies) section for how to setup a JDBC connector and Postgres driver.
+
+Postgres catalog supports the following options:
+- `name`: required, name of the catalog.
+- `default-database`: required, default database to connect to.
+- `username`: required, username of Postgres account.
+- `password`: required, password of the account.
+- `base-url`: required, should be of format `"jdbc:postgresql://:"`, and should not contain database name here.
+
+
+{% highlight java %}
+
+EnvironmentSettings settings = EnvironmentSettings.newInstance().inStreamingMode().build();
+TableEnvironment tableEnv = TableEnvironment.create(settings);
+
+String name = "mypg";
+String defaultDatabase = "mydb";
+String username = "...";
+String password = "...";
+String baseUrl = "..."
+
+JdbcCatalog catalog = new JdbcCatalog(name, defaultDatabase, username, password, baseUrl);
+tableEnv.registerCatalog("mypg", catalog);
+
+// set the JdbcCatalog as the current catalog of the session
+tableEnv.useCatalog("mypg");
+{% endhighlight %}
+
+
+{% highlight scala %}
+
+val settings = EnvironmentSettings.newInstance().inStreamingMode().build()
+val tableEnv = TableEnvironment.create(settings)
+
+val name = "mypg"
+val defaultDatabase = "mydb"
+val username = "..."
+val password = "..."
+val baseUrl = "..."
+
+val catalog = new JdbcCatalog(name, defaultDatabase, username, password, baseUrl)
+tableEnv.registerCatalog("mypg", catalog)
+
+// set the JdbcCatalog as the current catalog of the session
+tableEnv.useCatalog("mypg")
+{% endhighlight %}
+
+
+{% highlight yaml %}
+
+execution:
+ planner: blink
+ ...
+ current-catalog: mypg # set the JdbcCatalog as the current catalog of the session
+ current-database: mydb
+
+catalogs:
+ - name: mypg
+ type: jdbc
+ default-database: mydb
+ username: ...
+ password: ...
+ base-url: ...
+{% endhighlight %}
+
+
+
+#### PostgresSQL Metaspace Mapping
+
+PostgresSQL has an additional namespace as `schema` besides database. A Postgres instance can have multiple databases, each database can have multiple schemas with a default one named "public", each schema can have multiple tables.
+In Flink, when querying tables registered by Postgres catalog, users can use either `schema_name.table_name` or just `table_name`. The `schema_name` is optional and defaults to "public".
+
+Therefor the metaspace mapping between Flink Catalog and Postgres is as following:
+
+| Flink Catalog Metaspace Structure | Postgres Metaspace Structure |
+| :------------------------------------| :-----------------------------------|
+| catalog name (defined in Flink only) | N/A |
+| database name | database name |
+| table name | [schema_name.]table_name |
+
+The full path of Postgres table in Flink should be ``"..``"`` if schema is specified, note the `` should be escaped.
+
+Here are some examples to access Postgres tables:
+
+{% highlight sql %}
+-- scan table 'test_table' of 'public' schema (i.e. the default schema), the schema name can be omitted
+SELECT * FROM mypg.mydb.test_table;
+SELECT * FROM mydb.test_table;
+SELECT * FROM test_table;
+
+-- scan table 'test_table2' of 'custom_schema' schema,
+-- the custom schema can not be omitted and must be escaped with table.
+SELECT * FROM mypg.mydb.`custom_schema.test_table2`
+SELECT * FROM mydb.`custom_schema.test_table2`;
+SELECT * FROM `custom_schema.test_table2`;
+{% endhighlight %}
+
+Data Type Mapping
+----------------
+Flink supports connect to several databases which uses dialect like MySQL, PostgresSQL, Derby. The Derby dialect usually used for testing purpose. The field data type mappings from relational databases data types to Flink SQL data types are listed in the following table, the mapping table can help define JDBC table in Flink easily.
+
+
+
+
+
MySQL type
+
PostgreSQL type
+
Flink SQL type
+
+
+
+
+
TINYINT
+
+
TINYINT
+
+
+
+ SMALLINT
+ TINYINT UNSIGNED
+
+ SMALLINT
+ INT2
+ SMALLSERIAL
+ SERIAL2
+
SMALLINT
+
+
+
+ INT
+ MEDIUMINT
+ SMALLINT UNSIGNED
+
+ INTEGER
+ SERIAL
+
INT
+
+
+
+ BIGINT
+ INT UNSIGNED
+
+ BIGINT
+ BIGSERIAL
+
BIGINT
+
+
+
BIGINT UNSIGNED
+
+
DECIMAL(20, 0)
+
+
+
BIGINT
+
BIGINT
+
BIGINT
+
+
+
FLOAT
+
+ REAL
+ FLOAT4
+
FLOAT
+
+
+
+ DOUBLE
+ DOUBLE PRECISION
+
+ FLOAT8
+ DOUBLE PRECISION
+
DOUBLE
+
+
+
+ NUMERIC(p, s)
+ DECIMAL(p, s)
+
+ NUMERIC(p, s)
+ DECIMAL(p, s)
+
DECIMAL(p, s)
+
+
+
+ BOOLEAN
+ TINYINT(1)
+
BOOLEAN
+
BOOLEAN
+
+
+
DATE
+
DATE
+
DATE
+
+
+
TIME [(p)]
+
TIME [(p)] [WITHOUT TIMEZONE]
+
TIME [(p)] [WITHOUT TIMEZONE]
+
+
+
DATETIME [(p)]
+
TIMESTAMP [(p)] [WITHOUT TIMEZONE]
+
TIMESTAMP [(p)] [WITHOUT TIMEZONE]
+
+
+
+ CHAR(n)
+ VARCHAR(n)
+ TEXT
+
+ CHAR(n)
+ CHARACTER(n)
+ VARCHAR(n)
+ CHARACTER VARYING(n)
+ TEXT
+
STRING
+
+
+
+ BINARY
+ VARBINARY
+ BLOB
+
BYTEA
+
BYTES
+
+
+
+
ARRAY
+
ARRAY
+
+
+
+
+{% top %}
diff --git a/docs/dev/table/connectors/kafka.md b/docs/dev/table/connectors/kafka.md
new file mode 100644
index 00000000000000..f6ec308badecd3
--- /dev/null
+++ b/docs/dev/table/connectors/kafka.md
@@ -0,0 +1,216 @@
+---
+title: "Apache Kafka SQL Connector"
+nav-title: Kafka
+nav-parent_id: sql-connectors
+nav-pos: 2
+---
+
+
+Scan Source: Unbounded
+Sink: Streaming Append Mode
+
+* This will be replaced by the TOC
+{:toc}
+
+The Kafka connector allows for reading data from and writing data into Kafka topics.
+
+Dependencies
+------------
+
+Apache Flink ships with multiple Kafka connectors: universal, 0.10, and 0.11.
+This universal Kafka connector attempts to track the latest version of the Kafka client.
+The version of the client it uses may change between Flink releases.
+Modern Kafka clients are backwards compatible with broker versions 0.10.0 or later.
+For most users the universal Kafka connector is the most appropriate.
+However, for Kafka versions 0.11.x and 0.10.x, we recommend using the dedicated ``0.11`` and ``0.10`` connectors, respectively.
+For details on Kafka compatibility, please refer to the official [Kafka documentation](https://kafka.apache.org/protocol.html#protocol_compatibility).
+
+| Kafka Version | Maven dependency | SQL Client JAR |
+| :------------------ | :-------------------------------------------------------- | :----------------------|
+| universal | `flink-connector-kafka{{site.scala_version_suffix}}` | {% if site.is_stable %} [Download](https://repo.maven.apache.org/maven2/org/apache/flink/flink-sql-connector-kafka{{site.scala_version_suffix}}/{{site.version}}/flink-sql-connector-kafka{{site.scala_version_suffix}}-{{site.version}}.jar) {% else %} Only available for [stable releases]({{ site.stable_baseurl }}/dev/table/connectors/kafka.html) {% endif %} |
+| 0.11.x | `flink-connector-kafka-0.11{{site.scala_version_suffix}}` | {% if site.is_stable %} [Download](https://repo.maven.apache.org/maven2/org/apache/flink/flink-sql-connector-kafka-0.11{{site.scala_version_suffix}}/{{site.version}}/flink-sql-connector-kafka{{site.scala_version_suffix}}-{{site.version}}.jar) {% else %} Only available for [stable releases]({{ site.stable_baseurl }}/dev/table/connectors/kafka.html) {% endif %} |
+| 0.10.x | `flink-connector-kafka-0.10{{site.scala_version_suffix}}` | {% if site.is_stable %} [Download](https://repo.maven.apache.org/maven2/org/apache/flink/flink-sql-connector-kafka-0.10{{site.scala_version_suffix}}/{{site.version}}/flink-sql-connector-kafka{{site.scala_version_suffix}}-{{site.version}}.jar) {% else %} Only available for [stable releases]({{ site.stable_baseurl }}/dev/table/connectors/kafka.html) {% endif %} |
+
+The Kafka connectors are not currently part of the binary distribution.
+See how to link with them for cluster execution [here]({% link dev/project-configuration.md %}).
+
+How to create a Kafka table
+----------------
+
+The example below shows how to create a Kafka table:
+
+
Specify what connector to use, for Kafka the options are: 'kafka', 'kafka-0.11', 'kafka-0.10'.
+
+
+
topic
+
required
+
(none)
+
String
+
Topic name from which the table is read.
+
+
+
properties.bootstrap.servers
+
required
+
(none)
+
String
+
Comma separated list of Kafka brokers.
+
+
+
properties.group.id
+
required by source
+
(none)
+
String
+
The id of the consumer group for Kafka source, optional for Kafka sink.
+
+
+
format
+
required
+
(none)
+
String
+
The format used to deserialize and serialize Kafka messages.
+ The supported formats are 'csv', 'json', 'avro', 'debezium-json' and 'canal-json'.
+ Please refer to Formats page for more details and more format options.
+
+
+
+
scan.startup.mode
+
optional
+
group-offsets
+
String
+
Startup mode for Kafka consumer, valid values are 'earliest-offset', 'latest-offset', 'group-offsets', 'timestamp' and 'specific-offsets'.
+ See the following Start Reading Position for more details.
+
+
+
scan.startup.specific-offsets
+
optional
+
(none)
+
String
+
Specify offsets for each partition in case of 'specific-offsets' startup mode, e.g. 'partition:0,offset:42;partition:1,offset:300'.
+
+
+
+
scan.startup.timestamp-millis
+
optional
+
(none)
+
Long
+
Start from the specified epoch timestamp (milliseconds) used in case of 'timestamp' startup mode.
+
+
+
sink.partitioner
+
optional
+
(none)
+
String
+
Output partitioning from Flink's partitions into Kafka's partitions. Valid values are
+
+
fixed: each Flink partition ends up in at most one Kafka partition.
+
round-robin: a Flink partition is distributed to Kafka partitions round-robin.
+
Custom FlinkKafkaPartitioner subclass: e.g. 'org.mycompany.MyPartitioner'.
+
+
+
+
+
+
+Features
+----------------
+
+### Start Reading Position
+
+The config option `scan.startup.mode` specifies the startup mode for Kafka consumer. The valid enumerations are:
+
+
`group-offsets`: start from committed offsets in ZK / Kafka brokers of a specific consumer group.
+
`earliest-offset`: start from the earliest offset possible.
+
`latest-offset`: start from the latest offset.
+
`timestamp`: start from user-supplied timestamp for each partition.
+
`specific-offsets`: start from user-supplied specific offsets for each partition.
+
+
+The default option value is `group-offsets` which indicates to consume from last committed offsets in ZK / Kafka brokers.
+
+If `timestamp` is specified, another config option `scan.startup.timestamp-millis` is required to specify a specific startup timestamp in milliseconds since January 1, 1970 00:00:00.000 GMT.
+
+If `specific-offsets` is specified, another config option `scan.startup.specific-offsets` is required to specify specific startup offsets for each partition,
+e.g. an option value `partition:0,offset:42;partition:1,offset:300` indicates offset `42` for partition `0` and offset `300` for partition `1`.
+
+### Changelog Source
+
+Flink natively supports Kafka as a changelog source. If messages in Kafka topic is change event captured from other databases using CDC tools, then you can use a CDC format to interpret messages as INSERT/UPDATE/DELETE messages into Flink SQL system.
+Flink provides two CDC formats [debezium-json]({% link dev/table/connectors/formats/debezium.md %}) and [canal-json]({% link dev/table/connectors/formats/canal.md %}) to interpret change events captured by [Debezium](https://debezium.io/) and [Canal](https://github.com/alibaba/canal/wiki).
+The changelog source is a very useful feature in many cases, such as synchronizing incremental data from databases to other systems, auditing logs, materialized views on databases, temporal join changing history of a database table and so on.
+See more about how to use the CDC formats in [debezium-json]({% link dev/table/connectors/formats/debezium.md %}) and [canal-json]({% link dev/table/connectors/formats/canal.md %}).
+
+### Sink Partitioning
+
+The config option `sink.partitioner` specifies output partitioning from Flink's partitions into Kafka's partitions.
+By default, a Kafka sink writes to at most as many partitions as its own parallelism (each parallel instance of the sink writes to exactly one partition).
+In order to distribute the writes to more partitions or control the routing of rows into partitions, a custom sink partitioner can be provided. The `round-robin` partitioner is useful to avoid an unbalanced partitioning.
+However, it will cause a lot of network connections between all the Flink instances and all the Kafka brokers.
+
+### Consistency guarantees
+
+By default, a Kafka sink ingests data with at-least-once guarantees into a Kafka topic if the query is executed with [checkpointing enabled]({% link dev/stream/state/checkpointing.md %}#enabling-and-configuring-checkpointing).
+
+Data Type Mapping
+----------------
+
+Kafka stores message keys and values as bytes, so Kafka doesn't have schema or data types. The Kafka messages are deserialized and serialized by formats, e.g. csv, json, avro.
+Thus, the data type mapping is determined by specific formats. Please refer to [Formats]({% link dev/table/connectors/formats/index.md %}) pages for more details.
+
+{% top %}
diff --git a/docs/dev/table/connectors/kafka.zh.md b/docs/dev/table/connectors/kafka.zh.md
new file mode 100644
index 00000000000000..ffc373e24995f3
--- /dev/null
+++ b/docs/dev/table/connectors/kafka.zh.md
@@ -0,0 +1,216 @@
+---
+title: "Apache Kafka SQL Connector"
+nav-title: Kafka
+nav-parent_id: sql-connectors
+nav-pos: 2
+---
+
+
+Scan Source: Unbounded
+Sink: Streaming Append Mode
+
+* This will be replaced by the TOC
+{:toc}
+
+The Kafka connector allows for reading data from and writing data into Kafka topics.
+
+Dependencies
+------------
+
+Apache Flink ships with multiple Kafka connectors: universal, 0.10, and 0.11.
+This universal Kafka connector attempts to track the latest version of the Kafka client.
+The version of the client it uses may change between Flink releases.
+Modern Kafka clients are backwards compatible with broker versions 0.10.0 or later.
+For most users the universal Kafka connector is the most appropriate.
+However, for Kafka versions 0.11.x and 0.10.x, we recommend using the dedicated ``0.11`` and ``0.10`` connectors, respectively.
+For details on Kafka compatibility, please refer to the official [Kafka documentation](https://kafka.apache.org/protocol.html#protocol_compatibility).
+
+| Kafka Version | Maven dependency | SQL Client JAR |
+| :------------------ | :-------------------------------------------------------- | :----------------------|
+| universal | `flink-connector-kafka{{site.scala_version_suffix}}` | {% if site.is_stable %} [Download](https://repo.maven.apache.org/maven2/org/apache/flink/flink-sql-connector-kafka{{site.scala_version_suffix}}/{{site.version}}/flink-sql-connector-kafka{{site.scala_version_suffix}}-{{site.version}}.jar) {% else %} Only available for [stable releases]({{ site.stable_baseurl }}/zh/dev/table/connectors/kafka.html) {% endif %} |
+| 0.11.x | `flink-connector-kafka-0.11{{site.scala_version_suffix}}` | {% if site.is_stable %} [Download](https://repo.maven.apache.org/maven2/org/apache/flink/flink-sql-connector-kafka-0.11{{site.scala_version_suffix}}/{{site.version}}/flink-sql-connector-kafka{{site.scala_version_suffix}}-{{site.version}}.jar) {% else %} Only available for [stable releases]({{ site.stable_baseurl }}/zh/dev/table/connectors/kafka.html) {% endif %} |
+| 0.10.x | `flink-connector-kafka-0.10{{site.scala_version_suffix}}` | {% if site.is_stable %} [Download](https://repo.maven.apache.org/maven2/org/apache/flink/flink-sql-connector-kafka-0.10{{site.scala_version_suffix}}/{{site.version}}/flink-sql-connector-kafka{{site.scala_version_suffix}}-{{site.version}}.jar) {% else %} Only available for [stable releases]({{ site.stable_baseurl }}/zh/dev/table/connectors/kafka.html) {% endif %} |
+
+The Kafka connectors are not currently part of the binary distribution.
+See how to link with them for cluster execution [here]({% link dev/project-configuration.zh.md %}).
+
+How to create a Kafka table
+----------------
+
+The example below shows how to create a Kafka table:
+
+
Specify what connector to use, for Kafka the options are: 'kafka', 'kafka-0.11', 'kafka-0.10'.
+
+
+
topic
+
required
+
(none)
+
String
+
Topic name from which the table is read.
+
+
+
properties.bootstrap.servers
+
required
+
(none)
+
String
+
Comma separated list of Kafka brokers.
+
+
+
properties.group.id
+
required by source
+
(none)
+
String
+
The id of the consumer group for Kafka source, optional for Kafka sink.
+
+
+
format
+
required
+
(none)
+
String
+
The format used to deserialize and serialize Kafka messages.
+ The supported formats are 'csv', 'json', 'avro', 'debezium-json' and 'canal-json'.
+ Please refer to Formats page for more details and more format options.
+
+
+
+
scan.startup.mode
+
optional
+
group-offsets
+
String
+
Startup mode for Kafka consumer, valid values are 'earliest-offset', 'latest-offset', 'group-offsets', 'timestamp' and 'specific-offsets'.
+ See the following Start Reading Position for more details.
+
+
+
scan.startup.specific-offsets
+
optional
+
(none)
+
String
+
Specify offsets for each partition in case of 'specific-offsets' startup mode, e.g. 'partition:0,offset:42;partition:1,offset:300'.
+
+
+
+
scan.startup.timestamp-millis
+
optional
+
(none)
+
Long
+
Start from the specified epoch timestamp (milliseconds) used in case of 'timestamp' startup mode.
+
+
+
sink.partitioner
+
optional
+
(none)
+
String
+
Output partitioning from Flink's partitions into Kafka's partitions. Valid values are
+
+
fixed: each Flink partition ends up in at most one Kafka partition.
+
round-robin: a Flink partition is distributed to Kafka partitions round-robin.
+
Custom FlinkKafkaPartitioner subclass: e.g. 'org.mycompany.MyPartitioner'.
+
+
+
+
+
+
+Features
+----------------
+
+### Start Reading Position
+
+The config option `scan.startup.mode` specifies the startup mode for Kafka consumer. The valid enumerations are:
+
+
`group-offsets`: start from committed offsets in ZK / Kafka brokers of a specific consumer group.
+
`earliest-offset`: start from the earliest offset possible.
+
`latest-offset`: start from the latest offset.
+
`timestamp`: start from user-supplied timestamp for each partition.
+
`specific-offsets`: start from user-supplied specific offsets for each partition.
+
+
+The default option value is `group-offsets` which indicates to consume from last committed offsets in ZK / Kafka brokers.
+
+If `timestamp` is specified, another config option `scan.startup.timestamp-millis` is required to specify a specific startup timestamp in milliseconds since January 1, 1970 00:00:00.000 GMT.
+
+If `specific-offsets` is specified, another config option `scan.startup.specific-offsets` is required to specify specific startup offsets for each partition,
+e.g. an option value `partition:0,offset:42;partition:1,offset:300` indicates offset `42` for partition `0` and offset `300` for partition `1`.
+
+### Changelog Source
+
+Flink natively supports Kafka as a changelog source. If messages in Kafka topic is change event captured from other databases using CDC tools, then you can use a CDC format to interpret messages as INSERT/UPDATE/DELETE messages into Flink SQL system.
+Flink provides two CDC formats [debezium-json]({% link dev/table/connectors/formats/debezium.zh.md %}) and [canal-json]({% link dev/table/connectors/formats/canal.zh.md %}) to interpret change events captured by [Debezium](https://debezium.io/) and [Canal](https://github.com/alibaba/canal/wiki).
+The changelog source is a very useful feature in many cases, such as synchronizing incremental data from databases to other systems, auditing logs, materialized views on databases, temporal join changing history of a database table and so on.
+See more about how to use the CDC formats in [debezium-json]({% link dev/table/connectors/formats/debezium.zh.md %}) and [canal-json]({% link dev/table/connectors/formats/canal.zh.md %}).
+
+### Sink Partitioning
+
+The config option `sink.partitioner` specifies output partitioning from Flink's partitions into Kafka's partitions.
+By default, a Kafka sink writes to at most as many partitions as its own parallelism (each parallel instance of the sink writes to exactly one partition).
+In order to distribute the writes to more partitions or control the routing of rows into partitions, a custom sink partitioner can be provided. The `round-robin` partitioner is useful to avoid an unbalanced partitioning.
+However, it will cause a lot of network connections between all the Flink instances and all the Kafka brokers.
+
+### Consistency guarantees
+
+By default, a Kafka sink ingests data with at-least-once guarantees into a Kafka topic if the query is executed with [checkpointing enabled]({% link dev/stream/state/checkpointing.zh.md %}#enabling-and-configuring-checkpointing).
+
+Data Type Mapping
+----------------
+
+Kafka stores message keys and values as bytes, so Kafka doesn't have schema or data types. The Kafka messages are deserialized and serialized by formats, e.g. csv, json, avro.
+Thus, the data type mapping is determined by specific formats. Please refer to [Formats]({% link dev/table/connectors/formats/index.zh.md %}) pages for more details.
+
+{% top %}
diff --git a/docs/dev/table/connectors/print.md b/docs/dev/table/connectors/print.md
new file mode 100644
index 00000000000000..716655d970ef82
--- /dev/null
+++ b/docs/dev/table/connectors/print.md
@@ -0,0 +1,143 @@
+---
+title: "Print SQL Connector"
+nav-title: Print
+nav-parent_id: sql-connectors
+nav-pos: 11
+---
+
+
+Sink
+
+* This will be replaced by the TOC
+{:toc}
+
+The Print connector allows for writing every row to the standard output or standard error stream.
+
+It is designed for:
+
+- Easy test for streaming job.
+- Very useful in production debugging.
+
+Four possible format options:
+
+
+
+
+
Print
+
Condition1
+
Condition2
+
+
+
+
+
PRINT_IDENTIFIER:taskId> output
+
PRINT_IDENTIFIER provided
+
parallelism > 1
+
+
+
PRINT_IDENTIFIER> output
+
PRINT_IDENTIFIER provided
+
parallelism == 1
+
+
+
taskId> output
+
no PRINT_IDENTIFIER provided
+
parallelism > 1
+
+
+
output
+
no PRINT_IDENTIFIER provided
+
parallelism == 1
+
+
+
+
+The output string format is "$row_kind(f0,f1,f2...)", row_kind is the short string of [RowKind]({{ site.baseurl }}/api/java/org/apache/flink/types/RowKind.html), example is: "+I(1,1)".
+
+The Print connector is built-in.
+
+Attention Print sinks print records in runtime tasks, you need to observe the task log.
+
+How to create a Print table
+----------------
+
+
diff --git a/docs/dev/table/functions/udfs.md b/docs/dev/table/functions/udfs.md
index 26f557a54f570a..70516b7af76b82 100644
--- a/docs/dev/table/functions/udfs.md
+++ b/docs/dev/table/functions/udfs.md
@@ -22,327 +22,867 @@ specific language governing permissions and limitations
under the License.
-->
-User-defined functions are an important feature, because they significantly extend the expressiveness of queries.
+User-defined functions (UDFs) are extension points to call frequently used logic or custom logic that cannot be expressed otherwise in queries.
+
+User-defined functions can be implemented in a JVM language (such as Java or Scala) or Python. An implementer can use arbitrary third party libraries within a UDF. This page will focus on JVM-based languages.
* This will be replaced by the TOC
{:toc}
-Register User-Defined Functions
--------------------------------
-In most cases, a user-defined function must be registered before it can be used in an query. It is not necessary to register functions for the Scala Table API.
-
-Functions are registered at the `TableEnvironment` by calling a `registerFunction()` method. When a user-defined function is registered, it is inserted into the function catalog of the `TableEnvironment` such that the Table API or SQL parser can recognize and properly translate it.
+Overview
+--------
-Please find detailed examples of how to register and how to call each type of user-defined function
-(`ScalarFunction`, `TableFunction`, and `AggregateFunction`) in the following sub-sessions.
+Currently, Flink distinguishes between the following kinds of functions:
+- *Scalar functions* map scalar values to a new scalar value.
+- *Table functions* map scalar values to new rows.
+- *Aggregate functions* map scalar values of multiple rows to a new scalar value.
+- *Table aggregate functions* map scalar values of multiple rows to new rows.
+- *Async table functions* are special functions for table sources that perform a lookup.
-{% top %}
+Attention Scalar and table functions have been updated to the new type system based on [data types]({% link dev/table/types.md %}). Aggregating functions still use the old type system based on `TypeInformation`.
-Scalar Functions
-----------------
+The following example shows how to create a simple scalar function and how to call the function in both Table API and SQL.
-If a required scalar function is not contained in the built-in functions, it is possible to define custom, user-defined scalar functions for both the Table API and SQL. A user-defined scalar functions maps zero, one, or multiple scalar values to a new scalar value.
+For SQL queries, a function must always be registered under a name. For Table API, a function can be registered or directly used _inline_.
-
-In order to define a scalar function, one has to extend the base class `ScalarFunction` in `org.apache.flink.table.functions` and implement (one or more) evaluation methods. The behavior of a scalar function is determined by the evaluation method. An evaluation method must be declared publicly and named `eval`. The parameter types and return type of the evaluation method also determine the parameter and return types of the scalar function. Evaluation methods can also be overloaded by implementing multiple methods named `eval`. Evaluation methods can also support variable arguments, such as `eval(String... strs)`.
-
-The following example shows how to define your own hash code function, register it in the TableEnvironment, and call it in a query. Note that you can configure your scalar function via a constructor before it is registered:
+
{% highlight java %}
-public class HashCode extends ScalarFunction {
- private int factor = 12;
-
- public HashCode(int factor) {
- this.factor = factor;
+import org.apache.flink.table.api.*;
+import org.apache.flink.table.functions.ScalarFunction;
+import static org.apache.flink.table.api.Expressions.*;
+
+// define function logic
+public static class SubstringFunction extends ScalarFunction {
+ public String eval(String s, Integer begin, Integer end) {
+ return s.substring(begin, end);
}
-
- public int eval(String s) {
- return s.hashCode() * factor;
+}
+
+TableEnvironment env = TableEnvironment.create(...);
+
+// call function "inline" without registration in Table API
+env.from("MyTable").select(call(SubstringFunction.class, $("myField"), 5, 12));
+
+// register function
+env.createTemporarySystemFunction("SubstringFunction", SubstringFunction.class);
+
+// call registered function in Table API
+env.from("MyTable").select(call("SubstringFunction", $("myField"), 5, 12));
+
+// call registered function in SQL
+env.sqlQuery("SELECT SubstringFunction(myField, 5, 12) FROM MyTable");
+
+{% endhighlight %}
+
+
+
+{% highlight scala %}
+import org.apache.flink.table.api._
+import org.apache.flink.table.functions.ScalarFunction
+
+// define function logic
+class SubstringFunction extends ScalarFunction {
+ def eval(s: String, begin: Integer, end: Integer): String = {
+ s.substring(begin, end)
}
}
-BatchTableEnvironment tableEnv = BatchTableEnvironment.create(env);
+val env = TableEnvironment.create(...)
-// register the function
-tableEnv.registerFunction("hashCode", new HashCode(10));
+// call function "inline" without registration in Table API
+env.from("MyTable").select(call(classOf[SubstringFunction], $"myField", 5, 12))
-// use the function in Java Table API
-myTable.select("string, string.hashCode(), hashCode(string)");
+// register function
+env.createTemporarySystemFunction("SubstringFunction", classOf[SubstringFunction])
+
+// call registered function in Table API
+env.from("MyTable").select(call("SubstringFunction", $"myField", 5, 12))
+
+// call registered function in SQL
+env.sqlQuery("SELECT SubstringFunction(myField, 5, 12) FROM MyTable")
-// use the function in SQL API
-tableEnv.sqlQuery("SELECT string, hashCode(string) FROM MyTable");
{% endhighlight %}
+
+
+
-By default the result type of an evaluation method is determined by Flink's type extraction facilities. This is sufficient for basic types or simple POJOs but might be wrong for more complex, custom, or composite types. In these cases `TypeInformation` of the result type can be manually defined by overriding `ScalarFunction#getResultType()`.
+For interactive sessions, it is also possible to parameterize functions before using or
+registering them. In this case, function _instances_ instead of function _classes_ can be
+used as temporary functions.
-The following example shows an advanced example which takes the internal timestamp representation and also returns the internal timestamp representation as a long value. By overriding `ScalarFunction#getResultType()` we define that the returned long value should be interpreted as a `Types.TIMESTAMP` by the code generation.
+It requires that the parameters are serializable for shipping
+function instances to the cluster.
+
+
+
{% highlight java %}
-public static class TimestampModifier extends ScalarFunction {
- public long eval(long t) {
- return t % 1000;
+import org.apache.flink.table.api.*;
+import org.apache.flink.table.functions.ScalarFunction;
+import static org.apache.flink.table.api.Expressions.*;
+
+// define parameterizable function logic
+public static class SubstringFunction extends ScalarFunction {
+
+ private boolean endInclusive;
+
+ public SubstringFunction(boolean endInclusive) {
+ this.endInclusive = endInclusive;
}
- public TypeInformation> getResultType(Class>[] signature) {
- return Types.SQL_TIMESTAMP;
+ public String eval(String s, Integer begin, Integer end) {
+ return s.substring(a, endInclusive ? end + 1 : end);
}
}
-{% endhighlight %}
-
-
-In order to define a scalar function, one has to extend the base class `ScalarFunction` in `org.apache.flink.table.functions` and implement (one or more) evaluation methods. The behavior of a scalar function is determined by the evaluation method. An evaluation method must be declared publicly and named `eval`. The parameter types and return type of the evaluation method also determine the parameter and return types of the scalar function. Evaluation methods can also be overloaded by implementing multiple methods named `eval`. Evaluation methods can also support variable arguments, such as `@varargs def eval(str: String*)`.
+TableEnvironment env = TableEnvironment.create(...);
-The following example shows how to define your own hash code function, register it in the TableEnvironment, and call it in a query. Note that you can configure your scalar function via a constructor before it is registered:
+// call function "inline" without registration in Table API
+env.from("MyTable").select(call(new SubstringFunction(true), $("myField"), 5, 12));
+// register function
+env.createTemporarySystemFunction("SubstringFunction", new SubstringFunction(true));
+
+{% endhighlight %}
+
+
+
{% highlight scala %}
-// must be defined in static/object context
-class HashCode(factor: Int) extends ScalarFunction {
- def eval(s: String): Int = {
- s.hashCode() * factor
+import org.apache.flink.table.api._
+import org.apache.flink.table.functions.ScalarFunction
+
+// define parameterizable function logic
+class SubstringFunction(val endInclusive) extends ScalarFunction {
+ def eval(s: String, begin: Integer, end: Integer): String = {
+ s.substring(endInclusive ? end + 1 : end)
}
}
-val tableEnv = BatchTableEnvironment.create(env)
+val env = TableEnvironment.create(...)
-// use the function in Scala Table API
-val hashCode = new HashCode(10)
-myTable.select('string, hashCode('string))
+// call function "inline" without registration in Table API
+env.from("MyTable").select(call(new SubstringFunction(true), $"myField", 5, 12))
+
+// register function
+env.createTemporarySystemFunction("SubstringFunction", new SubstringFunction(true))
-// register and use the function in SQL
-tableEnv.registerFunction("hashCode", new HashCode(10))
-tableEnv.sqlQuery("SELECT string, hashCode(string) FROM MyTable")
{% endhighlight %}
+
+
+
+
+{% top %}
+
+Implementation Guide
+--------------------
+
+Attention This section only applies to scalar and table functions for now; until aggregate functions have been updated to the new type system.
+
+Independent of the kind of function, all user-defined functions follow some basic implementation principles.
+
+### Function Class
+
+An implementation class must extend from one of the available base classes (e.g. `org.apache.flink.table.functions.ScalarFunction`).
+
+The class must be declared `public`, not `abstract`, and should be globally accessible. Thus, non-static inner or anonymous classes are not allowed.
+
+For storing a user-defined function in a persistent catalog, the class must have a default constructor and must be instantiable during runtime.
+
+### Evaluation Methods
+
+The base class provides a set of methods that can be overridden such as `open()`, `close()`, or `isDeterministic()`.
+
+However, in addition to those declared methods, the main runtime logic that is applied to every incoming record must be implemented through specialized _evaluation methods_.
+
+Depending on the function kind, evaluation methods such as `eval()`, `accumulate()`, or `retract()` are called by code-generated operators during runtime.
+
+The methods must be declared `public` and take a well-defined set of arguments.
+
+Regular JVM method calling semantics apply. Therefore, it is possible to:
+- implement overloaded methods such as `eval(Integer)` and `eval(LocalDateTime)`,
+- use var-args such as `eval(Integer...)`,
+- use object inheritance such as `eval(Object)` that takes both `LocalDateTime` and `Integer`,
+- and combinations of the above such as `eval(Object...)` that takes all kinds of arguments.
+
+If you intend to implement functions in Scala, please add the `scala.annotation.varargs` annotation in
+case of variable arguments. Furthermore, it is recommended to use boxed primitives (e.g. `java.lang.Integer`
+instead of `Int`) to support `NULL`.
+
+The following snippets shows an example of an overloaded function:
+
+
+
+
+{% highlight java %}
+import org.apache.flink.table.functions.ScalarFunction;
-By default the result type of an evaluation method is determined by Flink's type extraction facilities. This is sufficient for basic types or simple POJOs but might be wrong for more complex, custom, or composite types. In these cases `TypeInformation` of the result type can be manually defined by overriding `ScalarFunction#getResultType()`.
+// function with overloaded evaluation methods
+public static class SumFunction extends ScalarFunction {
-The following example shows an advanced example which takes the internal timestamp representation and also returns the internal timestamp representation as a long value. By overriding `ScalarFunction#getResultType()` we define that the returned long value should be interpreted as a `Types.TIMESTAMP` by the code generation.
+ public Integer eval(Integer a, Integer b) {
+ return a + b;
+ }
+
+ public Integer eval(String a, String b) {
+ return Integer.valueOf(a) + Integer.valueOf();
+ }
+
+ public Integer eval(Double... d) {
+ double result = 0;
+ for (double value : d)
+ result += value;
+ return (int) result;
+ }
+}
+
+{% endhighlight %}
+
-In order to define a Python scalar function, one can extend the base class `ScalarFunction` in `pyflink.table.udf` and implement an evaluation method. The behavior of a Python scalar function is determined by the evaluation method which is named `eval`.
+
-The following example shows how to define your own Python hash code function, register it in the TableEnvironment, and call it in a query. Note that you can configure your scalar function via a constructor before it is registered:
+### Type Inference
-{% highlight python %}
-class HashCode(ScalarFunction):
- def __init__(self):
- self.factor = 12
+The table ecosystem (similar to the SQL standard) is a strongly typed API. Therefore, both function parameters and return types must be mapped to a [data type]({% link dev/table/types.md %}).
+
+From a logical perspective, the planner needs information about expected types, precision, and scale. From a JVM perspective, the planner needs information about how internal data structures are represented as JVM objects when calling a user-defined function.
+
+The logic for validating input arguments and deriving data types for both the parameters and the result of a function is summarized under the term _type inference_.
- def eval(self, s):
- return hash(s) * self.factor
+Flink's user-defined functions implement an automatic type inference extraction that derives data types from the function's class and its evaluation methods via reflection. If this implicit reflective extraction approach is not successful, the extraction process can be supported by annotating affected parameters, classes, or methods with `@DataTypeHint` and `@FunctionHint`. More examples on how to annotate functions are shown below.
-table_env = BatchTableEnvironment.create(env)
+If more advanced type inference logic is required, an implementer can explicitly override the `getTypeInference()` method in every user-defined function. However, the annotation approach is recommended because it keeps custom type inference logic close to the affected locations and falls back to the default behavior for the remaining implementation.
-# register the Python function
-table_env.register_function("hash_code", udf(HashCode(), DataTypes.BIGINT(), DataTypes.BIGINT()))
+#### Automatic Type Inference
-# use the function in Python Table API
-my_table.select("string, bigint, string.hash_code(), hash_code(string)")
+The automatic type inference inspects the function's class and evaluation methods to derive data types for the arguments and result of a function. `@DataTypeHint` and `@FunctionHint` annotations support the automatic extraction.
+
+For a full list of classes that can be implicitly mapped to a data type, see the [data type extraction section]({% link dev/table/types.md %}#data-type-extraction).
+
+**`@DataTypeHint`**
+
+In many scenarios, it is required to support the automatic extraction _inline_ for paramaters and return types of a function
+
+The following example shows how to use data type hints. More information can be found in the documentation of the annotation class.
+
+
+
+
+{% highlight java %}
+import org.apache.flink.table.annotation.DataTypeHint;
+import org.apache.flink.table.annotation.InputGroup;
+import org.apache.flink.table.functions.ScalarFunction;
+import org.apache.flink.types.Row;
+
+// function with overloaded evaluation methods
+public static class OverloadedFunction extends ScalarFunction {
+
+ // no hint required
+ public Long eval(long a, long b) {
+ return a + b;
+ }
+
+ // define the precision and scale of a decimal
+ public @DataTypeHint("DECIMAL(12, 3)") BigDecimal eval(double a, double b) {
+ return BigDecimal.valueOf(a + b);
+ }
+
+ // define a nested data type
+ @DataTypeHint("ROW")
+ public Row eval(int i) {
+ return Row.of(String.valueOf(i), Instant.ofEpochSecond(i));
+ }
+
+ // allow wildcard input and customly serialized output
+ @DataTypeHint(value = "RAW", bridgedTo = ByteBuffer.class)
+ public ByteBuffer eval(@DataTypeHint(inputGroup = InputGroup.ANY) Object o) {
+ return MyUtils.serializeToByteBuffer(o);
+ }
+}
-# use the function in SQL API
-table_env.sql_query("SELECT string, bigint, hash_code(bigint) FROM MyTable")
{% endhighlight %}
+
+
+
+{% highlight scala %}
+import org.apache.flink.table.annotation.DataTypeHint
+import org.apache.flink.table.annotation.InputGroup
+import org.apache.flink.table.functions.ScalarFunction
+import org.apache.flink.types.Row
+import scala.annotation.varargs
+
+// function with overloaded evaluation methods
+class OverloadedFunction extends ScalarFunction {
+
+ // no hint required
+ def eval(a: Long, b: Long): Long = {
+ a + b
+ }
-There are many ways to define a Python scalar function besides extending the base class `ScalarFunction`.
-Please refer to the [Python Scalar Function]({{ site.baseurl }}/dev/table/python/python_udfs.html#scalar-functions) documentation for more details.
+ // define the precision and scale of a decimal
+ @DataTypeHint("DECIMAL(12, 3)")
+ def eval(double a, double b): BigDecimal = {
+ java.lang.BigDecimal.valueOf(a + b)
+ }
+
+ // define a nested data type
+ @DataTypeHint("ROW")
+ def eval(Int i): Row = {
+ Row.of(java.lang.String.valueOf(i), java.time.Instant.ofEpochSecond(i))
+ }
+
+ // allow wildcard input and customly serialized output
+ @DataTypeHint(value = "RAW", bridgedTo = classOf[java.nio.ByteBuffer])
+ def eval(@DataTypeHint(inputGroup = InputGroup.ANY) Object o): java.nio.ByteBuffer = {
+ MyUtils.serializeToByteBuffer(o)
+ }
+}
+
+{% endhighlight %}
+
-{% top %}
+**`@FunctionHint`**
-Table Functions
----------------
+In some scenarios, it is desirable that one evaluation method handles multiple different data types at the same time. Furthermore, in some scenarios, overloaded evaluation methods have a common result type that should be declared only once.
+
+The `@FunctionHint` annotation can provide a mapping from argument data types to a result data type. It enables annotating entire function classes or evaluation methods for input, accumulator, and result data types. One or more annotations can be declared on top of a class or individually for each evaluation method for overloading function signatures. All hint parameters are optional. If a parameter is not defined, the default reflection-based extraction is used. Hint parameters defined on top of a function class are inherited by all evaluation methods.
-Similar to a user-defined scalar function, a user-defined table function takes zero, one, or multiple scalar values as input parameters. However in contrast to a scalar function, it can return an arbitrary number of rows as output instead of a single value. The returned rows may consist of one or more columns.
+The following example shows how to use function hints. More information can be found in the documentation of the annotation class.
-
-In order to define a table function one has to extend the base class `TableFunction` in `org.apache.flink.table.functions` and implement (one or more) evaluation methods. The behavior of a table function is determined by its evaluation methods. An evaluation method must be declared `public` and named `eval`. The `TableFunction` can be overloaded by implementing multiple methods named `eval`. The parameter types of the evaluation methods determine all valid parameters of the table function. Evaluation methods can also support variable arguments, such as `eval(String... strs)`. The type of the returned table is determined by the generic type of `TableFunction`. Evaluation methods emit output rows using the protected `collect(T)` method.
-In the Table API, a table function is used with `.joinLateral` or `.leftOuterJoinLateral`. The `joinLateral` operator (cross) joins each row from the outer table (table on the left of the operator) with all rows produced by the table-valued function (which is on the right side of the operator). The `leftOuterJoinLateral` operator joins each row from the outer table (table on the left of the operator) with all rows produced by the table-valued function (which is on the right side of the operator) and preserves outer rows for which the table function returns an empty table. In SQL use `LATERAL TABLE()` with CROSS JOIN and LEFT JOIN with an ON TRUE join condition (see examples below).
+
+{% highlight java %}
+import org.apache.flink.table.annotation.DataTypeHint;
+import org.apache.flink.table.annotation.FunctionHint;
+import org.apache.flink.table.functions.TableFunction;
+import org.apache.flink.types.Row;
+
+// function with overloaded evaluation methods
+// but globally defined output type
+@FunctionHint(output = @DataTypeHint("ROW"))
+public static class OverloadedFunction extends TableFunction {
+
+ public void eval(int a, int b) {
+ collect(Row.of("Sum", a + b));
+ }
+
+ // overloading of arguments is still possible
+ public void eval() {
+ collect(Row.of("Empty args", -1));
+ }
+}
+
+// decouples the type inference from evaluation methods,
+// the type inference is entirely determined by the function hints
+@FunctionHint(
+ input = [@DataTypeHint("INT"), @DataTypeHint("INT")],
+ output = @DataTypeHint("INT")
+)
+@FunctionHint(
+ input = [@DataTypeHint("LONG"), @DataTypeHint("LONG")],
+ output = @DataTypeHint("LONG")
+)
+@FunctionHint(
+ input = [],
+ output = @DataTypeHint("BOOLEAN")
+)
+public static class OverloadedFunction extends TableFunction
+
+
+{% highlight scala %}
+
+import org.apache.flink.table.annotation.DataTypeHint
+import org.apache.flink.table.annotation.FunctionHint
+import org.apache.flink.table.functions.TableFunction
+import org.apache.flink.types.Row
+
+// function with overloaded evaluation methods
+// but globally defined output type
+@FunctionHint(output = @DataTypeHint("ROW"))
+class OverloadedFunction extends TableFunction[Row] {
+
+ def eval(a: Int, b: Int): Unit = {
+ collect(Row.of("Sum", Int.box(a + b)))
+ }
+
+ // overloading of arguments is still possible
+ def eval(): Unit = {
+ collect(Row.of("Empty args", Int.box(-1)))
+ }
+}
+
+// decouples the type inference from evaluation methods,
+// the type inference is entirely determined by the function hints
+@FunctionHint(
+ input = Array(@DataTypeHint("INT"), @DataTypeHint("INT")),
+ output = @DataTypeHint("INT")
+)
+@FunctionHint(
+ input = Array(@DataTypeHint("LONG"), @DataTypeHint("LONG")),
+ output = @DataTypeHint("LONG")
+)
+@FunctionHint(
+ input = Array(),
+ output = @DataTypeHint("BOOLEAN")
+)
+class OverloadedFunction extends TableFunction[AnyRef] {
+
+ // an implementer just needs to make sure that a method exists
+ // that can be called by the JVM
+ @varargs
+ def eval(o: AnyRef*) = {
+ if (o.length == 0) {
+ collect(Boolean.box(false))
+ }
+ collect(o(0))
+ }
+}
+
+{% endhighlight %}
+
+
+
+
+#### Custom Type Inference
+
+For most scenarios, `@DataTypeHint` and `@FunctionHint` should be sufficient to model user-defined functions. However, by overriding the automatic type inference defined in `getTypeInference()`, implementers can create arbitrary functions that behave like built-in system functions.
+
+The following example implemented in Java illustrates the potential of a custom type inference logic. It uses a string literal argument to determine the result type of a function. The function takes two string arguments: the first argument represents the string to be parsed, the second argument represents the target type.
-The following example shows how to define table-valued function, register it in the TableEnvironment, and call it in a query. Note that you can configure your table function via a constructor before it is registered:
+
+
{% highlight java %}
-// The generic type "Tuple2" determines the schema of the returned table as (String, Integer).
-public class Split extends TableFunction> {
- private String separator = " ";
-
- public Split(String separator) {
- this.separator = separator;
+import org.apache.flink.table.api.DataTypes;
+import org.apache.flink.table.catalog.DataTypeFactory;
+import org.apache.flink.table.functions.ScalarFunction;
+import org.apache.flink.table.types.inference.TypeInference;
+import org.apache.flink.types.Row;
+
+public static class LiteralFunction extends ScalarFunction {
+ public Object eval(String s, String type) {
+ switch (type) {
+ case "INT":
+ return Integer.valueOf(s);
+ case "DOUBLE":
+ return Double.valueOf(s);
+ case "STRING":
+ default:
+ return s;
}
-
- public void eval(String str) {
- for (String s : str.split(separator)) {
- // use collect(...) to emit a row
- collect(new Tuple2(s, s.length()));
+ }
+
+ // the automatic, reflection-based type inference is disabled and
+ // replaced by the following logic
+ @Override
+ public TypeInference getTypeInference(DataTypeFactory typeFactory) {
+ return TypeInference.newBuilder()
+ // specify typed arguments
+ // parameters will be casted implicitly to those types if necessary
+ .typedArguments(DataTypes.STRING(), DataTypes.STRING())
+ // specify a strategy for the result data type of the function
+ .outputTypeStrategy(callContext -> {
+ if (!callContext.isArgumentLiteral(1) || callContext.isArgumentNull(1)) {
+ throw callContext.newValidationError("Literal expected for second argument.");
+ }
+ // return a data type based on a literal
+ final String literal = callContext.getArgumentValue(1, String.class).orElse("STRING");
+ switch (literal) {
+ case "INT":
+ return Optional.of(DataTypes.INT().notNull());
+ case "DOUBLE":
+ return Optional.of(DataTypes.DOUBLE().notNull());
+ case "STRING":
+ default:
+ return Optional.of(DataTypes.STRING());
}
+ })
+ .build();
+ }
+}
+
+{% endhighlight %}
+
+
+
+
+### Runtime Integration
+
+Sometimes it might be necessary for a user-defined function to get global runtime information or do some setup/clean-up work before the actual work. User-defined functions provide `open()` and `close()` methods that can be overridden and provide similar functionality as the methods in `RichFunction` of DataStream API.
+
+The `open()` method is called once before the evaluation method. The `close()` method after the last call to the evaluation method.
+
+The `open()` method provides a `FunctionContext` that contains information about the context in which user-defined functions are executed, such as the metric group, the distributed cache files, or the global job parameters.
+
+The following information can be obtained by calling the corresponding methods of `FunctionContext`:
+
+| Method | Description |
+| :------------------------------------ | :----------------------------------------------------- |
+| `getMetricGroup()` | Metric group for this parallel subtask. |
+| `getCachedFile(name)` | Local temporary file copy of a distributed cache file. |
+| `getJobParameter(name, defaultValue)` | Global job parameter value associated with given key. |
+
+The following example snippet shows how to use `FunctionContext` in a scalar function for accessing a global job parameter:
+
+
+
+
+{% highlight java %}
+import org.apache.flink.table.api.*;
+import org.apache.flink.table.functions.FunctionContext;
+import org.apache.flink.table.functions.ScalarFunction;
+
+public static class HashCodeFunction extends ScalarFunction {
+
+ private int factor = 0;
+
+ @Override
+ public void open(FunctionContext context) throws Exception {
+ // access the global "hashcode_factor" parameter
+ // "12" would be the default value if the parameter does not exist
+ factor = Integer.parseInt(context.getJobParameter("hashcode_factor", "12"));
+ }
+
+ public int eval(String s) {
+ return s.hashCode() * factor;
}
}
-BatchTableEnvironment tableEnv = BatchTableEnvironment.create(env);
-Table myTable = ... // table schema: [a: String]
+TableEnvironment env = TableEnvironment.create(...);
-// Register the function.
-tableEnv.registerFunction("split", new Split("#"));
+// add job parameter
+env.getConfig().addJobParameter("hashcode_factor", "31");
-// Use the table function in the Java Table API. "as" specifies the field names of the table.
-myTable.joinLateral("split(a) as (word, length)")
- .select("a, word, length");
-myTable.leftOuterJoinLateral("split(a) as (word, length)")
- .select("a, word, length");
+// register the function
+env.createTemporarySystemFunction("hashCode", HashCodeFunction.class);
+
+// use the function
+env.sqlQuery("SELECT myField, hashCode(myField) FROM MyTable");
-// Use the table function in SQL with LATERAL and TABLE keywords.
-// CROSS JOIN a table function (equivalent to "join" in Table API).
-tableEnv.sqlQuery("SELECT a, word, length FROM MyTable, LATERAL TABLE(split(a)) as T(word, length)");
-// LEFT JOIN a table function (equivalent to "leftOuterJoin" in Table API).
-tableEnv.sqlQuery("SELECT a, word, length FROM MyTable LEFT JOIN LATERAL TABLE(split(a)) as T(word, length) ON TRUE");
{% endhighlight %}
-In order to define a table function one has to extend the base class `TableFunction` in `org.apache.flink.table.functions` and implement (one or more) evaluation methods. The behavior of a table function is determined by its evaluation methods. An evaluation method must be declared `public` and named `eval`. The `TableFunction` can be overloaded by implementing multiple methods named `eval`. The parameter types of the evaluation methods determine all valid parameters of the table function. Evaluation methods can also support variable arguments, such as `eval(String... strs)`. The type of the returned table is determined by the generic type of `TableFunction`. Evaluation methods emit output rows using the protected `collect(T)` method.
+{% highlight scala %}
+import org.apache.flink.table.api._
+import org.apache.flink.table.functions.FunctionContext
+import org.apache.flink.table.functions.ScalarFunction
-In the Table API, a table function is used with `.joinLateral` or `.leftOuterJoinLateral`. The `joinLateral` operator (cross) joins each row from the outer table (table on the left of the operator) with all rows produced by the table-valued function (which is on the right side of the operator). The `leftOuterJoinLateral` operator joins each row from the outer table (table on the left of the operator) with all rows produced by the table-valued function (which is on the right side of the operator) and preserves outer rows for which the table function returns an empty table. In SQL use `LATERAL TABLE()` with CROSS JOIN and LEFT JOIN with an ON TRUE join condition (see examples below).
+class HashCodeFunction extends ScalarFunction {
-The following example shows how to define table-valued function, register it in the TableEnvironment, and call it in a query. Note that you can configure your table function via a constructor before it is registered:
+ private var factor: Int = 0
-{% highlight scala %}
-// The generic type "(String, Int)" determines the schema of the returned table as (String, Integer).
-class Split(separator: String) extends TableFunction[(String, Int)] {
- def eval(str: String): Unit = {
- // use collect(...) to emit a row.
- str.split(separator).foreach(x => collect((x, x.length)))
+ override def open(context: FunctionContext): Unit = {
+ // access the global "hashcode_factor" parameter
+ // "12" would be the default value if the parameter does not exist
+ factor = context.getJobParameter("hashcode_factor", "12").toInt
+ }
+
+ def eval(s: String): Int = {
+ s.hashCode * factor
}
}
-val tableEnv = BatchTableEnvironment.create(env)
-val myTable = ... // table schema: [a: String]
+val env = TableEnvironment.create(...)
+
+// add job parameter
+env.getConfig.addJobParameter("hashcode_factor", "31")
-// Use the table function in the Scala Table API (Note: No registration required in Scala Table API).
-val split = new Split("#")
-// "as" specifies the field names of the generated table.
-myTable.joinLateral(split('a) as ('word, 'length)).select('a, 'word, 'length)
-myTable.leftOuterJoinLateral(split('a) as ('word, 'length)).select('a, 'word, 'length)
+// register the function
+env.createTemporarySystemFunction("hashCode", classOf[HashCodeFunction])
-// Register the table function to use it in SQL queries.
-tableEnv.registerFunction("split", new Split("#"))
+// use the function
+env.sqlQuery("SELECT myField, hashCode(myField) FROM MyTable")
-// Use the table function in SQL with LATERAL and TABLE keywords.
-// CROSS JOIN a table function (equivalent to "join" in Table API)
-tableEnv.sqlQuery("SELECT a, word, length FROM MyTable, LATERAL TABLE(split(a)) as T(word, length)")
-// LEFT JOIN a table function (equivalent to "leftOuterJoin" in Table API)
-tableEnv.sqlQuery("SELECT a, word, length FROM MyTable LEFT JOIN LATERAL TABLE(split(a)) as T(word, length) ON TRUE")
{% endhighlight %}
-**IMPORTANT:** Do not implement TableFunction as a Scala object. Scala object is a singleton and will cause concurrency issues.
-
-In order to define a Python table function, one can extend the base class `TableFunction` in `pyflink.table.udtf` and Implement an evaluation method. The behavior of a Python table function is determined by the evaluation method which is named eval.
+
-In the Python Table API, a Python table function is used with `.join_lateral` or `.left_outer_join_lateral`. The `join_lateral` operator (cross) joins each row from the outer table (table on the left of the operator) with all rows produced by the table-valued function (which is on the right side of the operator). The `left_outer_join_lateral` operator joins each row from the outer table (table on the left of the operator) with all rows produced by the table-valued function (which is on the right side of the operator) and preserves outer rows for which the table function returns an empty table. In SQL use `LATERAL TABLE()` with CROSS JOIN and LEFT JOIN with an ON TRUE join condition (see examples below).
+{% top %}
-The following example shows how to define a Python table function, registered it in the TableEnvironment, and call it in a query. Note that you can configure your table function via a constructor before it is registered:
+Scalar Functions
+----------------
-{% highlight python %}
-class Split(TableFunction):
- def eval(self, string):
- for s in string.split(" "):
- yield s, len(s)
+A user-defined scalar function maps zero, one, or multiple scalar values to a new scalar value. Any data type listed in the [data types section]({% link dev/table/types.md %}) can be used as a parameter or return type of an evaluation method.
+
+In order to define a scalar function, one has to extend the base class `ScalarFunction` in `org.apache.flink.table.functions` and implement one or more evaluation methods named `eval(...)`.
+
+The following example shows how to define your own hash code function and call it in a query. See the [Implementation Guide](#implementation-guide) for more details.
-env = StreamExecutionEnvironment.get_execution_environment()
-table_env = StreamTableEnvironment.create(env)
-my_table = ... # type: Table, table schema: [a: String]
+
+
+
+{% highlight java %}
+import org.apache.flink.table.annotation.InputGroup;
+import org.apache.flink.table.api.*;
+import org.apache.flink.table.functions.ScalarFunction;
+import static org.apache.flink.table.api.Expressions.*;
-# register the Python Table Function
-table_env.register_function("split", udtf(Split(), DataTypes.STRING(), [DataTypes.STRING(), DataTypes.INT()]))
+public static class HashFunction extends ScalarFunction {
+
+ // take any data type and return INT
+ public int eval(@DataTypeHint(inputGroup = InputGroup.ANY) Object o) {
+ return o.hashCode();
+ }
+}
-# use the Python Table Function in Python Table API
-my_table.join_lateral("split(a) as (word, length)")
-my_table.left_outer_join_lateral("split(a) as (word, length)")
+TableEnvironment env = TableEnvironment.create(...);
-# use the Python Table function in SQL API
-table_env.sql_query("SELECT a, word, length FROM MyTable, LATERAL TABLE(split(a)) as T(word, length)")
-table_env.sql_query("SELECT a, word, length FROM MyTable LEFT JOIN LATERAL TABLE(split(a)) as T(word, length) ON TRUE")
+// call function "inline" without registration in Table API
+env.from("MyTable").select(call(HashFunction.class, $("myField")));
+
+// register function
+env.createTemporarySystemFunction("HashFunction", HashFunction.class);
+
+// call registered function in Table API
+env.from("MyTable").select(call("HashFunction", $("myField")));
+
+// call registered function in SQL
+env.sqlQuery("SELECT HashFunction(myField) FROM MyTable");
{% endhighlight %}
+
-There are many ways to define a Python table function besides extending the base class `TableFunction`.
-Please refer to the [Python Table Function]({{ site.baseurl }}/dev/table/python/python_udfs.html#table-functions) documentation for more details.
-
+
+{% highlight scala %}
+import org.apache.flink.table.annotation.InputGroup
+import org.apache.flink.table.api._
+import org.apache.flink.table.functions.ScalarFunction
+
+class HashFunction extends ScalarFunction {
+
+ // take any data type and return INT
+ def eval(@DataTypeHint(inputGroup = InputGroup.ANY) o: AnyRef): Int {
+ return o.hashCode();
+ }
+}
+
+val env = TableEnvironment.create(...)
+
+// call function "inline" without registration in Table API
+env.from("MyTable").select(call(classOf[HashFunction], $"myField"))
+
+// register function
+env.createTemporarySystemFunction("HashFunction", classOf[HashFunction])
+
+// call registered function in Table API
+env.from("MyTable").select(call("HashFunction", $"myField"))
+
+// call registered function in SQL
+env.sqlQuery("SELECT HashFunction(myField) FROM MyTable")
+
+{% endhighlight %}
+
-
-
-Please note that POJO types do not have a deterministic field order. Therefore, you cannot rename the fields of POJO returned by a table function using `AS`.
+If you intend to implement or call functions in Python, please refer to the [Python Scalar Functions]({% link dev/table/python/python_udfs.md %}#scalar-functions) documentation for more details.
-By default the result type of a `TableFunction` is determined by Flink’s automatic type extraction facilities. This works well for basic types and simple POJOs but might be wrong for more complex, custom, or composite types. In such a case, the type of the result can be manually specified by overriding `TableFunction#getResultType()` which returns its `TypeInformation`.
+{% top %}
-The following example shows an example of a `TableFunction` that returns a `Row` type which requires explicit type information. We define that the returned table type should be `RowTypeInfo(String, Integer)` by overriding `TableFunction#getResultType()`.
+Table Functions
+---------------
-{% highlight java %}
-public class CustomTypeSplit extends TableFunction {
- public void eval(String str) {
- for (String s : str.split(" ")) {
- Row row = new Row(2);
- row.setField(0, s);
- row.setField(1, s.length());
- collect(row);
- }
- }
+Similar to a user-defined scalar function, a user-defined table function takes zero, one, or multiple scalar values as input arguments. However, in contrast to a scalar function, it can return an arbitrary number of rows (or structured types) as output instead of a single value. The returned record may consist of one or more fields. If an output record consists of only one field, the structured record can be omitted and a scalar value can be emitted. It will be wrapped into an implicit row by the runtime.
- @Override
- public TypeInformation getResultType() {
- return Types.ROW(Types.STRING(), Types.INT());
+In order to define a table function, one has to extend the base class `TableFunction` in `org.apache.flink.table.functions` and implement one or more evaluation methods named `eval(...)`. Similar to other functions, input and output data types are automatically extracted using reflection. This includes the generic argument `T` of the class for determining an output data type. In contrast to scalar functions, the evaluation method itself must not have a return type, instead, table functions provide a `collect(T)` method that can be called within every evaluation method for emitting zero, one, or more records.
+
+In the Table API, a table function is used with `.joinLateral(...)` or `.leftOuterJoinLateral(...)`. The `joinLateral` operator (cross) joins each row from the outer table (table on the left of the operator) with all rows produced by the table-valued function (which is on the right side of the operator). The `leftOuterJoinLateral` operator joins each row from the outer table (table on the left of the operator) with all rows produced by the table-valued function (which is on the right side of the operator) and preserves outer rows for which the table function returns an empty table.
+
+In SQL, use `LATERAL TABLE()` with `JOIN` or `LEFT JOIN` with an `ON TRUE` join condition.
+
+The following example shows how to define your own split function and call it in a query. See the [Implementation Guide](#implementation-guide) for more details.
+
+
+
+
+{% highlight java %}
+import org.apache.flink.table.annotation.DataTypeHint;
+import org.apache.flink.table.annotation.FunctionHint;
+import org.apache.flink.table.api.*;
+import org.apache.flink.table.functions.TableFunction;
+import org.apache.flink.types.Row;
+import static org.apache.flink.table.api.Expressions.*;
+
+@FunctionHint(output = @DataTypeHint("ROW"))
+public static class SplitFunction extends TableFunction {
+
+ public void eval(String str) {
+ for (String s : str.split(" ")) {
+ // use collect(...) to emit a row
+ collect(Row.of(s, s.length()));
}
+ }
}
-{% endhighlight %}
-
-
-Please note that POJO types do not have a deterministic field order. Therefore, you cannot rename the fields of POJO returned by a table function using `AS`.
+TableEnvironment env = TableEnvironment.create(...);
+
+// call function "inline" without registration in Table API
+env
+ .from("MyTable")
+ .joinLateral(call(SplitFunction.class, $("myField")))
+ .select($("myField"), $("word"), $("length"));
+env
+ .from("MyTable")
+ .leftOuterJoinLateral(call(SplitFunction.class, $("myField")))
+ .select($("myField"), $("word"), $("length"));
-By default the result type of a `TableFunction` is determined by Flink’s automatic type extraction facilities. This works well for basic types and simple POJOs but might be wrong for more complex, custom, or composite types. In such a case, the type of the result can be manually specified by overriding `TableFunction#getResultType()` which returns its `TypeInformation`.
+// rename fields of the function in Table API
+env
+ .from("MyTable")
+ .leftOuterJoinLateral(call(SplitFunction.class, $("myField")).as("newWord", "newLength"))
+ .select($("myField"), $("newWord"), $("newLength"));
-The following example shows an example of a `TableFunction` that returns a `Row` type which requires explicit type information. We define that the returned table type should be `RowTypeInfo(String, Integer)` by overriding `TableFunction#getResultType()`.
+// register function
+env.createTemporarySystemFunction("SplitFunction", SplitFunction.class);
+
+// call registered function in Table API
+env
+ .from("MyTable")
+ .joinLateral(call("SplitFunction", $("myField")))
+ .select($("myField"), $("word"), $("length"));
+env
+ .from("MyTable")
+ .leftOuterJoinLateral(call("SplitFunction", $("myField")))
+ .select($("myField"), $("word"), $("length"));
+
+// call registered function in SQL
+env.sqlQuery(
+ "SELECT myField, word, length " +
+ "FROM MyTable, LATERAL TABLE(SplitFunction(myField))");
+env.sqlQuery(
+ "SELECT myField, word, length " +
+ "FROM MyTable " +
+ "LEFT JOIN LATERAL TABLE(SplitFunction(myField)) ON TRUE");
+
+// rename fields of the function in SQL
+env.sqlQuery(
+ "SELECT myField, newWord, newLength " +
+ "FROM MyTable " +
+ "LEFT JOIN LATERAL TABLE(SplitFunction(myField)) AS T(newWord, newLength) ON TRUE");
+{% endhighlight %}
+
+
+
{% highlight scala %}
-class CustomTypeSplit extends TableFunction[Row] {
- def eval(str: String): Unit = {
- str.split(" ").foreach({ s =>
- val row = new Row(2)
- row.setField(0, s)
- row.setField(1, s.length)
- collect(row)
- })
- }
+import org.apache.flink.table.annotation.DataTypeHint
+import org.apache.flink.table.annotation.FunctionHint
+import org.apache.flink.table.api._
+import org.apache.flink.table.functions.TableFunction
+import org.apache.flink.types.Row
- override def getResultType: TypeInformation[Row] = {
- Types.ROW(Types.STRING, Types.INT)
+@FunctionHint(output = @DataTypeHint("ROW"))
+class SplitFunction extends TableFunction[Row] {
+
+ def eval(str: String): Unit = {
+ // use collect(...) to emit a row
+ str.split(" ").foreach(s => collect(Row.of(s, s.length)))
}
}
+
+val env = TableEnvironment.create(...)
+
+// call function "inline" without registration in Table API
+env
+ .from("MyTable")
+ .joinLateral(call(classOf[SplitFunction], $"myField")
+ .select($"myField", $"word", $"length")
+env
+ .from("MyTable")
+ .leftOuterJoinLateral(call(classOf[SplitFunction], $"myField"))
+ .select($"myField", $"word", $"length")
+
+// rename fields of the function in Table API
+env
+ .from("MyTable")
+ .leftOuterJoinLateral(call(classOf[SplitFunction], $"myField").as("newWord", "newLength"))
+ .select($"myField", $"newWord", $"newLength")
+
+// register function
+env.createTemporarySystemFunction("SplitFunction", classOf[SplitFunction])
+
+// call registered function in Table API
+env
+ .from("MyTable")
+ .joinLateral(call("SplitFunction", $"myField"))
+ .select($"myField", $"word", $"length")
+env
+ .from("MyTable")
+ .leftOuterJoinLateral(call("SplitFunction", $"myField"))
+ .select($"myField", $"word", $"length")
+
+// call registered function in SQL
+env.sqlQuery(
+ "SELECT myField, word, length " +
+ "FROM MyTable, LATERAL TABLE(SplitFunction(myField))");
+env.sqlQuery(
+ "SELECT myField, word, length " +
+ "FROM MyTable " +
+ "LEFT JOIN LATERAL TABLE(SplitFunction(myField)) ON TRUE")
+
+// rename fields of the function in SQL
+env.sqlQuery(
+ "SELECT myField, newWord, newLength " +
+ "FROM MyTable " +
+ "LEFT JOIN LATERAL TABLE(SplitFunction(myField)) AS T(newWord, newLength) ON TRUE")
+
{% endhighlight %}
+
-{% top %}
+If you intend to implement functions in Scala, do not implement a table function as a Scala `object`. Scala `object`s are singletons and will cause concurrency issues.
+If you intend to implement or call functions in Python, please refer to the [Python Table Functions]({% link dev/table/python/python_udfs.md %}#table-functions) documentation for more details.
+
+{% top %}
Aggregation Functions
---------------------
@@ -821,7 +1361,6 @@ t_env.sql_query("SELECT user, wAvg(points, level) AS avgPoints FROM userScores G
-
-{% top %}
-
-Best Practices for Implementing UDFs
-------------------------------------
-
-The Table API and SQL code generation internally tries to work with primitive values as much as possible. A user-defined function can introduce much overhead through object creation, casting, and (un)boxing. Therefore, it is highly recommended to declare parameters and result types as primitive types instead of their boxed classes. `Types.DATE` and `Types.TIME` can also be represented as `int`. `Types.TIMESTAMP` can be represented as `long`.
-
-We recommended that user-defined functions should be written by Java instead of Scala as Scala types pose a challenge for Flink's type extractor.
-
-{% top %}
-
-Integrating UDFs with the Runtime
----------------------------------
-
-Sometimes it might be necessary for a user-defined function to get global runtime information or do some setup/clean-up work before the actual work. User-defined functions provide `open()` and `close()` methods that can be overridden and provide similar functionality as the methods in `RichFunction` of DataSet or DataStream API.
-
-The `open()` method is called once before the evaluation method. The `close()` method after the last call to the evaluation method.
-
-The `open()` method provides a `FunctionContext` that contains information about the context in which user-defined functions are executed, such as the metric group, the distributed cache files, or the global job parameters.
-
-The following information can be obtained by calling the corresponding methods of `FunctionContext`:
-
-| Method | Description |
-| :------------------------------------ | :----------------------------------------------------- |
-| `getMetricGroup()` | Metric group for this parallel subtask. |
-| `getCachedFile(name)` | Local temporary file copy of a distributed cache file. |
-| `getJobParameter(name, defaultValue)` | Global job parameter value associated with given key. |
-
-The following example snippet shows how to use `FunctionContext` in a scalar function for accessing a global job parameter:
-
-
-
-{% highlight java %}
-public class HashCode extends ScalarFunction {
-
- private int factor = 0;
-
- @Override
- public void open(FunctionContext context) throws Exception {
- // access "hashcode_factor" parameter
- // "12" would be the default value if parameter does not exist
- factor = Integer.valueOf(context.getJobParameter("hashcode_factor", "12"));
- }
-
- public int eval(String s) {
- return s.hashCode() * factor;
- }
-}
-
-ExecutionEnvironment env = ExecutionEnvironment.getExecutionEnvironment();
-BatchTableEnvironment tableEnv = BatchTableEnvironment.create(env);
-
-// set job parameter
-Configuration conf = new Configuration();
-conf.setString("hashcode_factor", "31");
-env.getConfig().setGlobalJobParameters(conf);
-
-// register the function
-tableEnv.registerFunction("hashCode", new HashCode());
-
-// use the function in Java Table API
-myTable.select("string, string.hashCode(), hashCode(string)");
-
-// use the function in SQL
-tableEnv.sqlQuery("SELECT string, HASHCODE(string) FROM MyTable");
-{% endhighlight %}
-
-
-
-{% highlight scala %}
-object hashCode extends ScalarFunction {
-
- var hashcode_factor = 12
-
- override def open(context: FunctionContext): Unit = {
- // access "hashcode_factor" parameter
- // "12" would be the default value if parameter does not exist
- hashcode_factor = context.getJobParameter("hashcode_factor", "12").toInt
- }
-
- def eval(s: String): Int = {
- s.hashCode() * hashcode_factor
- }
-}
-
-val tableEnv = BatchTableEnvironment.create(env)
-
-// use the function in Scala Table API
-myTable.select('string, hashCode('string))
-
-// register and use the function in SQL
-tableEnv.registerFunction("hashCode", hashCode)
-tableEnv.sqlQuery("SELECT string, HASHCODE(string) FROM MyTable")
-{% endhighlight %}
-
-
-
-
{% top %}
-
diff --git a/docs/dev/table/functions/udfs.zh.md b/docs/dev/table/functions/udfs.zh.md
index c0e7c67a20bbdd..4e3a6be02c9c09 100644
--- a/docs/dev/table/functions/udfs.zh.md
+++ b/docs/dev/table/functions/udfs.zh.md
@@ -22,364 +22,887 @@ specific language governing permissions and limitations
under the License.
-->
-User-defined functions are an important feature, because they significantly extend the expressiveness of queries.
+自定义函数(UDF)是一种扩展开发机制,可以用来在查询语句里调用难以用其他方式表达的频繁使用或自定义的逻辑。
+
+自定义函数可以用 JVM 语言(例如 Java 或 Scala)或 Python 实现,实现者可以在 UDF 中使用任意第三方库,本文聚焦于使用 JVM 语言开发自定义函数。
* This will be replaced by the TOC
{:toc}
-Register User-Defined Functions
--------------------------------
-In most cases, a user-defined function must be registered before it can be used in an query. It is not necessary to register functions for the Scala Table API.
-
-Functions are registered at the `TableEnvironment` by calling a `registerFunction()` method. When a user-defined function is registered, it is inserted into the function catalog of the `TableEnvironment` such that the Table API or SQL parser can recognize and properly translate it.
+概述
+--------
-Please find detailed examples of how to register and how to call each type of user-defined function
-(`ScalarFunction`, `TableFunction`, and `AggregateFunction`) in the following sub-sessions.
+当前 Flink 有如下几种函数:
+- *标量函数* 将标量值转换成一个新标量值;
+- *表值函数* 将标量值转换成新的行数据;
+- *聚合函数* 将多行数据里的标量值转换成一个新标量值;
+- *表值聚合函数* 将多行数据里的标量值转换成新的行数据;
+- *异步表值函数* 是异步查询外部数据系统的特殊函数。
-{% top %}
+注意 标量和表值函数已经使用了新的基于[数据类型]({% link dev/table/types.zh.md %})的类型系统,聚合函数仍然使用基于 `TypeInformation` 的旧类型系统。
-Scalar Functions
-----------------
+以下示例展示了如何创建一个基本的标量函数,以及如何在 Table API 和 SQL 里调用这个函数。
-If a required scalar function is not contained in the built-in functions, it is possible to define custom, user-defined scalar functions for both the Table API and SQL. A user-defined scalar functions maps zero, one, or multiple scalar values to a new scalar value.
+函数用于 SQL 查询前要先经过注册;而在用于 Table API 时,函数可以先注册后调用,也可以 _内联_ 后直接使用。
-
-In order to define a scalar function, one has to extend the base class `ScalarFunction` in `org.apache.flink.table.functions` and implement (one or more) evaluation methods. The behavior of a scalar function is determined by the evaluation method. An evaluation method must be declared publicly and named `eval`. The parameter types and return type of the evaluation method also determine the parameter and return types of the scalar function. Evaluation methods can also be overloaded by implementing multiple methods named `eval`. Evaluation methods can also support variable arguments, such as `eval(String... strs)`.
-
-The following example shows how to define your own hash code function, register it in the TableEnvironment, and call it in a query. Note that you can configure your scalar function via a constructor before it is registered:
+
+{% highlight scala %}
+import org.apache.flink.table.api._
+import org.apache.flink.table.functions.ScalarFunction
+
+// define function logic
+class SubstringFunction extends ScalarFunction {
+ def eval(s: String, begin: Integer, end: Integer): String = {
+ s.substring(begin, end)
}
}
-BatchTableEnvironment tableEnv = BatchTableEnvironment.create(env);
+val env = TableEnvironment.create(...)
+
+// 在 Table API 里不经注册直接“内联”调用函数
+env.from("MyTable").select(call(classOf[SubstringFunction], $"myField", 5, 12))
-// register the function
-tableEnv.registerFunction("hashCode", new HashCode(10));
+// 注册函数
+env.createTemporarySystemFunction("SubstringFunction", classOf[SubstringFunction])
-// use the function in Java Table API
-myTable.select("string, string.hashCode(), hashCode(string)");
+// 在 Table API 里调用注册好的函数
+env.from("MyTable").select(call("SubstringFunction", $"myField", 5, 12))
+
+// 在 SQL 里调用注册好的函数
+env.sqlQuery("SELECT SubstringFunction(myField, 5, 12) FROM MyTable")
-// use the function in SQL API
-tableEnv.sqlQuery("SELECT string, hashCode(string) FROM MyTable");
{% endhighlight %}
+
-By default the result type of an evaluation method is determined by Flink's type extraction facilities. This is sufficient for basic types or simple POJOs but might be wrong for more complex, custom, or composite types. In these cases `TypeInformation` of the result type can be manually defined by overriding `ScalarFunction#getResultType()`.
+
+
+对于交互式会话,还可以在使用或注册函数之前对其进行参数化,这样可以把函数 _实例_ 而不是函数 _类_ 用作临时函数。
-The following example shows an advanced example which takes the internal timestamp representation and also returns the internal timestamp representation as a long value. By overriding `ScalarFunction#getResultType()` we define that the returned long value should be interpreted as a `Types.TIMESTAMP` by the code generation.
+为确保函数实例可应用于集群环境,参数必须是可序列化的。
+
+
+
{% highlight java %}
-public static class TimestampModifier extends ScalarFunction {
- public long eval(long t) {
- return t % 1000;
+import org.apache.flink.table.api.*;
+import org.apache.flink.table.functions.ScalarFunction;
+import static org.apache.flink.table.api.Expressions.*;
+
+// 定义可参数化的函数逻辑
+public static class SubstringFunction extends ScalarFunction {
+
+ private boolean endInclusive;
+
+ public SubstringFunction(boolean endInclusive) {
+ this.endInclusive = endInclusive;
}
- public TypeInformation> getResultType(Class>[] signature) {
- return Types.SQL_TIMESTAMP;
+ public String eval(String s, Integer begin, Integer end) {
+ return s.substring(a, endInclusive ? end + 1 : end);
}
}
-{% endhighlight %}
-
-
-In order to define a scalar function, one has to extend the base class `ScalarFunction` in `org.apache.flink.table.functions` and implement (one or more) evaluation methods. The behavior of a scalar function is determined by the evaluation method. An evaluation method must be declared publicly and named `eval`. The parameter types and return type of the evaluation method also determine the parameter and return types of the scalar function. Evaluation methods can also be overloaded by implementing multiple methods named `eval`. Evaluation methods can also support variable arguments, such as `@varargs def eval(str: String*)`.
+TableEnvironment env = TableEnvironment.create(...);
-The following example shows how to define your own hash code function, register it in the TableEnvironment, and call it in a query. Note that you can configure your scalar function via a constructor before it is registered:
+// 在 Table API 里不经注册直接“内联”调用函数
+env.from("MyTable").select(call(new SubstringFunction(true), $("myField"), 5, 12));
+// 注册函数
+env.createTemporarySystemFunction("SubstringFunction", new SubstringFunction(true));
+
+{% endhighlight %}
+
+
+
{% highlight scala %}
-// must be defined in static/object context
-class HashCode(factor: Int) extends ScalarFunction {
- def eval(s: String): Int = {
- s.hashCode() * factor
+import org.apache.flink.table.api._
+import org.apache.flink.table.functions.ScalarFunction
+
+// 定义可参数化的函数逻辑
+class SubstringFunction(val endInclusive) extends ScalarFunction {
+ def eval(s: String, begin: Integer, end: Integer): String = {
+ s.substring(endInclusive ? end + 1 : end)
}
}
-val tableEnv = BatchTableEnvironment.create(env)
+val env = TableEnvironment.create(...)
+
+// 在 Table API 里不经注册直接“内联”调用函数
+env.from("MyTable").select(call(new SubstringFunction(true), $"myField", 5, 12))
-// use the function in Scala Table API
-val hashCode = new HashCode(10)
-myTable.select('string, hashCode('string))
+// 注册函数
+env.createTemporarySystemFunction("SubstringFunction", new SubstringFunction(true))
-// register and use the function in SQL
-tableEnv.registerFunction("hashCode", new HashCode(10))
-tableEnv.sqlQuery("SELECT string, hashCode(string) FROM MyTable")
{% endhighlight %}
+
-By default the result type of an evaluation method is determined by Flink's type extraction facilities. This is sufficient for basic types or simple POJOs but might be wrong for more complex, custom, or composite types. In these cases `TypeInformation` of the result type can be manually defined by overriding `ScalarFunction#getResultType()`.
+
-The following example shows an advanced example which takes the internal timestamp representation and also returns the internal timestamp representation as a long value. By overriding `ScalarFunction#getResultType()` we define that the returned long value should be interpreted as a `Types.TIMESTAMP` by the code generation.
+{% top %}
+开发指南
+--------
+
+注意在聚合函数使用新的类型系统前,本节仅适用于标量和表值函数。
+
+所有的自定义函数都遵循一些基本的实现原则。
+
+### 函数类
+
+实现类必须继承自合适的基类之一(例如 `org.apache.flink.table.functions.ScalarFunction` )。
+
+该类必须声明为 `public` ,而不是 `abstract` ,并且可以被全局访问。不允许使用非静态内部类或匿名类。
+
+为了将自定义函数存储在持久化的 catalog 中,该类必须具有默认构造器,且在运行时可实例化。
+
+### 求值方法
+
+基类提供了一组可以被重写的方法,例如 `open()`、 `close()` 或 `isDeterministic()` 。
+
+但是,除了上述方法之外,作用于每条传入记录的主要逻辑还必须通过专门的 _求值方法_ 来实现。
+
+根据函数的种类,后台生成的运算符会在运行时调用诸如 `eval()`、`accumulate()` 或 `retract()` 之类的求值方法。
+
+这些方法必须声明为 `public` ,并带有一组定义明确的参数。
+
+常规的 JVM 方法调用语义是适用的。因此可以:
+- 实现重载的方法,例如 `eval(Integer)` 和 `eval(LocalDateTime)`;
+- 使用变长参数,例如 `eval(Integer...)`;
+- 使用对象继承,例如 `eval(Object)` 可接受 `LocalDateTime` 和 `Integer` 作为参数;
+- 也可组合使用,例如 `eval(Object...)` 可接受所有类型的参数。
+
+以下代码片段展示了一个重载函数的示例:
+
+
+
+
+{% highlight java %}
+import org.apache.flink.table.functions.ScalarFunction;
+
+// 有多个重载求值方法的函数
+public static class SumFunction extends ScalarFunction {
+
+ public Integer eval(Integer a, Integer b) {
+ return a + b;
+ }
+
+ public Integer eval(String a, String b) {
+ return Integer.valueOf(a) + Integer.valueOf();
+ }
+
+ public Integer eval(Double... d) {
+ double result = 0;
+ for (double value : d)
+ result += value;
+ return (int) result;
+ }
+}
+
+{% endhighlight %}
+
-In order to define a Python scalar function, one can extend the base class `ScalarFunction` in `pyflink.table.udf` and implement an evaluation method. The behavior of a Python scalar function is determined by the evaluation method which is named `eval`.
+
-The following example shows how to define your own Python hash code function, register it in the TableEnvironment, and call it in a query. Note that you can configure your scalar function via a constructor before it is registered:
+### 类型推导
-{% highlight python %}
-class HashCode(ScalarFunction):
- def __init__(self):
- self.factor = 12
+Table(类似于 SQL 标准)是一种强类型的 API。因此,函数的参数和返回类型都必须映射到[数据类型]({%link dev/table/types.zh.md %})。
+
+从逻辑角度看,Planner 需要知道数据类型、精度和小数位数;从 JVM 角度来看,Planner 在调用自定义函数时需要知道如何将内部数据结构表示为 JVM 对象。
+
+术语 _类型推导_ 概括了意在验证输入值、派生出参数/返回值数据类型的逻辑。
+
+Flink 自定义函数实现了自动的类型推导提取,通过反射从函数的类及其求值方法中派生数据类型。如果这种隐式的反射提取方法不成功,则可以通过使用 `@DataTypeHint` 和 `@FunctionHint` 注解相关参数、类或方法来支持提取过程,下面展示了有关如何注解函数的例子。
- def eval(self, s):
- return hash(s) * self.factor
+如果需要更高级的类型推导逻辑,实现者可以在每个自定义函数中显式重写 `getTypeInference()` 方法。但是,建议使用注解方式,因为它可使自定义类型推导逻辑保持在受影响位置附近,而在其他位置则保持默认状态。
-table_env = BatchTableEnvironment.create(env)
-# register the Python function
-table_env.register_function("hash_code", udf(HashCode(), DataTypes.BIGINT(), DataTypes.BIGINT()))
+#### 自动类型推导
-# use the function in Python Table API
-my_table.select("string, bigint, string.hash_code(), hash_code(string)")
+自动类型推导会检查函数的类和求值方法,派生出函数参数和结果的数据类型, `@DataTypeHint` 和 `@FunctionHint` 注解支持自动类型推导。
+
+有关可以隐式映射到数据类型的类的完整列表,请参阅[数据类型]({%link dev/table/types.zh.md %}#数据类型注解)。
+
+**`@DataTypeHint`**
+
+在许多情况下,需要支持以 _内联_ 方式自动提取出函数参数、返回值的类型。
+
+以下例子展示了如何使用 `@DataTypeHint`,详情可参考该注解类的文档。
+
+
+
+
+{% highlight java %}
+import org.apache.flink.table.annotation.DataTypeHint;
+import org.apache.flink.table.annotation.InputGroup;
+import org.apache.flink.table.functions.ScalarFunction;
+import org.apache.flink.types.Row;
+
+// 有多个重载求值方法的函数
+public static class OverloadedFunction extends ScalarFunction {
+
+ // no hint required
+ public Long eval(long a, long b) {
+ return a + b;
+ }
+
+ // 定义 decimal 的精度和小数位
+ public @DataTypeHint("DECIMAL(12, 3)") BigDecimal eval(double a, double b) {
+ return BigDecimal.valueOf(a + b);
+ }
+
+ // 定义嵌套数据类型
+ @DataTypeHint("ROW")
+ public Row eval(int i) {
+ return Row.of(String.valueOf(i), Instant.ofEpochSecond(i));
+ }
+
+ // 允许任意类型的符入,并输出序列化定制后的值
+ @DataTypeHint(value = "RAW", bridgedTo = ByteBuffer.class)
+ public ByteBuffer eval(@DataTypeHint(inputGroup = InputGroup.ANY) Object o) {
+ return MyUtils.serializeToByteBuffer(o);
+ }
+}
-# use the function in SQL API
-table_env.sql_query("SELECT string, bigint, hash_code(bigint) FROM MyTable")
{% endhighlight %}
+
-There are many ways to define a Python scalar function besides extending the base class `ScalarFunction`.
-Please refer to the [Python Scalar Function]({{ site.baseurl }}/zh/dev/table/python/python_udfs.html#scalar-functions) documentation for more details.
+
-{% top %}
+**`@FunctionHint`**
-Table Functions
----------------
+有时我们希望一种求值方法可以同时处理多种数据类型,有时又要求对重载的多个求值方法仅声明一次通用的结果类型。
+
+`@FunctionHint` 注解可以提供从入参数据类型到结果数据类型的映射,它可以在整个函数类或求值方法上注解输入、累加器和结果的数据类型。可以在类顶部声明一个或多个注解,也可以为类的所有求值方法分别声明一个或多个注解。所有的 hint 参数都是可选的,如果未定义参数,则使用默认的基于反射的类型提取。在函数类顶部定义的 hint 参数被所有求值方法继承。
-Similar to a user-defined scalar function, a user-defined table function takes zero, one, or multiple scalar values as input parameters. However in contrast to a scalar function, it can return an arbitrary number of rows as output instead of a single value. The returned rows may consist of one or more columns.
+以下例子展示了如何使用 `@FunctionHint`,详情可参考该注解类的文档。
-
-In order to define a table function one has to extend the base class `TableFunction` in `org.apache.flink.table.functions` and implement (one or more) evaluation methods. The behavior of a table function is determined by its evaluation methods. An evaluation method must be declared `public` and named `eval`. The `TableFunction` can be overloaded by implementing multiple methods named `eval`. The parameter types of the evaluation methods determine all valid parameters of the table function. Evaluation methods can also support variable arguments, such as `eval(String... strs)`. The type of the returned table is determined by the generic type of `TableFunction`. Evaluation methods emit output rows using the protected `collect(T)` method.
+
+{% highlight java %}
+import org.apache.flink.table.annotation.DataTypeHint;
+import org.apache.flink.table.annotation.FunctionHint;
+import org.apache.flink.table.functions.TableFunction;
+import org.apache.flink.types.Row;
+
+// 为函数类的所有求值方法指定同一个输出类型
+@FunctionHint(output = @DataTypeHint("ROW"))
+public static class OverloadedFunction extends TableFunction {
-In the Table API, a table function is used with `.joinLateral` or `.leftOuterJoinLateral`. The `joinLateral` operator (cross) joins each row from the outer table (table on the left of the operator) with all rows produced by the table-valued function (which is on the right side of the operator). The `leftOuterJoinLateral` operator joins each row from the outer table (table on the left of the operator) with all rows produced by the table-valued function (which is on the right side of the operator) and preserves outer rows for which the table function returns an empty table. In SQL use `LATERAL TABLE()` with CROSS JOIN and LEFT JOIN with an ON TRUE join condition (see examples below).
+ public void eval(int a, int b) {
+ collect(Row.of("Sum", a + b));
+ }
-The following example shows how to define table-valued function, register it in the TableEnvironment, and call it in a query. Note that you can configure your table function via a constructor before it is registered:
+ // overloading of arguments is still possible
+ public void eval() {
+ collect(Row.of("Empty args", -1));
+ }
+}
+// 解耦类型推导与求值方法,类型推导完全取决于 FunctionHint
+@FunctionHint(
+ input = [@DataTypeHint("INT"), @DataTypeHint("INT")],
+ output = @DataTypeHint("INT")
+)
+@FunctionHint(
+ input = [@DataTypeHint("LONG"), @DataTypeHint("LONG")],
+ output = @DataTypeHint("LONG")
+)
+@FunctionHint(
+ input = [],
+ output = @DataTypeHint("BOOLEAN")
+)
+public static class OverloadedFunction extends TableFunction
+
+
+{% highlight scala %}
+
+import org.apache.flink.table.annotation.DataTypeHint
+import org.apache.flink.table.annotation.FunctionHint
+import org.apache.flink.table.functions.TableFunction
+import org.apache.flink.types.Row
+
+// 为函数类的所有求值方法指定同一个输出类型
+@FunctionHint(output = @DataTypeHint("ROW"))
+class OverloadedFunction extends TableFunction[Row] {
+
+ def eval(a: Int, b: Int): Unit = {
+ collect(Row.of("Sum", Int.box(a + b)))
+ }
+
+ // overloading of arguments is still possible
+ def eval(): Unit = {
+ collect(Row.of("Empty args", Int.box(-1)))
+ }
+}
+
+// 解耦类型推导与求值方法,类型推导完全取决于 @FunctionHint
+@FunctionHint(
+ input = Array(@DataTypeHint("INT"), @DataTypeHint("INT")),
+ output = @DataTypeHint("INT")
+)
+@FunctionHint(
+ input = Array(@DataTypeHint("LONG"), @DataTypeHint("LONG")),
+ output = @DataTypeHint("LONG")
+)
+@FunctionHint(
+ input = Array(),
+ output = @DataTypeHint("BOOLEAN")
+)
+class OverloadedFunction extends TableFunction[AnyRef] {
+
+ // an implementer just needs to make sure that a method exists
+ // that can be called by the JVM
+ @varargs
+ def eval(o: AnyRef*) = {
+ if (o.length == 0) {
+ collect(Boolean.box(false))
+ }
+ collect(o(0))
+ }
+}
+
+{% endhighlight %}
+
+{% highlight java %}
+import org.apache.flink.table.api.*;
+import org.apache.flink.table.functions.FunctionContext;
+import org.apache.flink.table.functions.ScalarFunction;
+
+public static class HashCodeFunction extends ScalarFunction {
+
+ private int factor = 0;
+
+ @Override
+ public void open(FunctionContext context) throws Exception {
+ // 获取参数 "hashcode_factor"
+ // 如果不存在,则使用默认值 "12"
+ factor = Integer.parseInt(context.getJobParameter("hashcode_factor", "12"));
+ }
+
+ public int eval(String s) {
+ return s.hashCode() * factor;
}
}
-BatchTableEnvironment tableEnv = BatchTableEnvironment.create(env);
-Table myTable = ... // table schema: [a: String]
+TableEnvironment env = TableEnvironment.create(...);
+
+// 设置任务参数
+env.getConfig().addJobParameter("hashcode_factor", "31");
-// Register the function.
-tableEnv.registerFunction("split", new Split("#"));
+// 注册函数
+env.createTemporarySystemFunction("hashCode", HashCodeFunction.class);
-// Use the table function in the Java Table API. "as" specifies the field names of the table.
-myTable.joinLateral("split(a) as (word, length)")
- .select("a, word, length");
-myTable.leftOuterJoinLateral("split(a) as (word, length)")
- .select("a, word, length");
+// 调用函数
+env.sqlQuery("SELECT myField, hashCode(myField) FROM MyTable");
-// Use the table function in SQL with LATERAL and TABLE keywords.
-// CROSS JOIN a table function (equivalent to "join" in Table API).
-tableEnv.sqlQuery("SELECT a, word, length FROM MyTable, LATERAL TABLE(split(a)) as T(word, length)");
-// LEFT JOIN a table function (equivalent to "leftOuterJoin" in Table API).
-tableEnv.sqlQuery("SELECT a, word, length FROM MyTable LEFT JOIN LATERAL TABLE(split(a)) as T(word, length) ON TRUE");
{% endhighlight %}
+{% highlight scala %}
+import org.apache.flink.table.api._
+import org.apache.flink.table.functions.FunctionContext
+import org.apache.flink.table.functions.ScalarFunction
-In order to define a table function one has to extend the base class `TableFunction` in `org.apache.flink.table.functions` and implement (one or more) evaluation methods. The behavior of a table function is determined by its evaluation methods. An evaluation method must be declared `public` and named `eval`. The `TableFunction` can be overloaded by implementing multiple methods named `eval`. The parameter types of the evaluation methods determine all valid parameters of the table function. Evaluation methods can also support variable arguments, such as `eval(String... strs)`. The type of the returned table is determined by the generic type of `TableFunction`. Evaluation methods emit output rows using the protected `collect(T)` method.
+class HashCodeFunction extends ScalarFunction {
-In the Table API, a table function is used with `.joinLateral` or `.leftOuterJoinLateral`. The `joinLateral` operator (cross) joins each row from the outer table (table on the left of the operator) with all rows produced by the table-valued function (which is on the right side of the operator). The `leftOuterJoinLateral` operator joins each row from the outer table (table on the left of the operator) with all rows produced by the table-valued function (which is on the right side of the operator) and preserves outer rows for which the table function returns an empty table. In SQL use `LATERAL TABLE()` with CROSS JOIN and LEFT JOIN with an ON TRUE join condition (see examples below).
+ private var factor: Int = 0
-The following example shows how to define table-valued function, register it in the TableEnvironment, and call it in a query. Note that you can configure your table function via a constructor before it is registered:
+ override def open(context: FunctionContext): Unit = {
+ // 获取参数 "hashcode_factor"
+ // 如果不存在,则使用默认值 "12"
+ factor = context.getJobParameter("hashcode_factor", "12").toInt
+ }
-{% highlight scala %}
-// The generic type "(String, Int)" determines the schema of the returned table as (String, Integer).
-class Split(separator: String) extends TableFunction[(String, Int)] {
- def eval(str: String): Unit = {
- // use collect(...) to emit a row.
- str.split(separator).foreach(x => collect((x, x.length)))
+ def eval(s: String): Int = {
+ s.hashCode * factor
}
}
-val tableEnv = BatchTableEnvironment.create(env)
-val myTable = ... // table schema: [a: String]
+val env = TableEnvironment.create(...)
-// Use the table function in the Scala Table API (Note: No registration required in Scala Table API).
-val split = new Split("#")
-// "as" specifies the field names of the generated table.
-myTable.joinLateral(split('a) as ('word, 'length)).select('a, 'word, 'length)
-myTable.leftOuterJoinLateral(split('a) as ('word, 'length)).select('a, 'word, 'length)
+// 设置任务参数
+env.getConfig.addJobParameter("hashcode_factor", "31")
-// Register the table function to use it in SQL queries.
-tableEnv.registerFunction("split", new Split("#"))
+// 注册函数
+env.createTemporarySystemFunction("hashCode", classOf[HashCodeFunction])
+
+// 调用函数
+env.sqlQuery("SELECT myField, hashCode(myField) FROM MyTable")
-// Use the table function in SQL with LATERAL and TABLE keywords.
-// CROSS JOIN a table function (equivalent to "join" in Table API)
-tableEnv.sqlQuery("SELECT a, word, length FROM MyTable, LATERAL TABLE(split(a)) as T(word, length)")
-// LEFT JOIN a table function (equivalent to "leftOuterJoin" in Table API)
-tableEnv.sqlQuery("SELECT a, word, length FROM MyTable LEFT JOIN LATERAL TABLE(split(a)) as T(word, length) ON TRUE")
{% endhighlight %}
-**IMPORTANT:** Do not implement TableFunction as a Scala object. Scala object is a singleton and will cause concurrency issues.
-
-In order to define a Python table function, one can extend the base class `TableFunction` in `pyflink.table.udtf` and Implement an evaluation method. The behavior of a Python table function is determined by the evaluation method which is named eval.
+
-In the Python Table API, a Python table function is used with `.join_lateral` or `.left_outer_join_lateral`. The `join_lateral` operator (cross) joins each row from the outer table (table on the left of the operator) with all rows produced by the table-valued function (which is on the right side of the operator). The `left_outer_join_lateral` operator joins each row from the outer table (table on the left of the operator) with all rows produced by the table-valued function (which is on the right side of the operator) and preserves outer rows for which the table function returns an empty table. In SQL use `LATERAL TABLE()` with CROSS JOIN and LEFT JOIN with an ON TRUE join condition (see examples below).
+{% top %}
-The following example shows how to define a Python table function, registered it in the TableEnvironment, and call it in a query. Note that you can configure your table function via a constructor before it is registered:
+标量函数
+----------------
-{% highlight python %}
-class Split(TableFunction):
- def eval(self, string):
- for s in string.split(" "):
- yield s, len(s)
+自定义标量函数可以把 0 到多个标量值映射成 1 个标量值,[数据类型]({%link dev/table/types.zh.md %})里列出的任何数据类型都可作为求值方法的参数和返回值类型。
+
+想要实现自定义标量函数,你需要扩展 `org.apache.flink.table.functions` 里面的 `ScalarFunction` 并且实现一个或者多个求值方法。标量函数的行为取决于你写的求值方法。求值方法必须是 `public` 的,而且名字必须是 `eval`。
-env = StreamExecutionEnvironment.get_execution_environment()
-table_env = StreamTableEnvironment.create(env)
-my_table = ... # type: Table, table schema: [a: String]
+下面的例子展示了如何实现一个求哈希值的函数并在查询里调用它,详情可参考[开发指南](#开发指南):
-# register the Python Table Function
-table_env.register_function("split", udtf(Split(), DataTypes.STRING(), [DataTypes.STRING(), DataTypes.INT()]))
+
+
+
+{% highlight java %}
+import org.apache.flink.table.annotation.InputGroup;
+import org.apache.flink.table.api.*;
+import org.apache.flink.table.functions.ScalarFunction;
+import static org.apache.flink.table.api.Expressions.*;
-# use the Python Table Function in Python Table API
-my_table.join_lateral("split(a) as (word, length)")
-my_table.left_outer_join_lateral("split(a) as (word, length)")
+public static class HashFunction extends ScalarFunction {
-# use the Python Table function in SQL API
-table_env.sql_query("SELECT a, word, length FROM MyTable, LATERAL TABLE(split(a)) as T(word, length)")
-table_env.sql_query("SELECT a, word, length FROM MyTable LEFT JOIN LATERAL TABLE(split(a)) as T(word, length) ON TRUE")
+ // 接受任意类型输入,返回 INT 型输出
+ public int eval(@DataTypeHint(inputGroup = InputGroup.ANY) Object o) {
+ return o.hashCode();
+ }
+}
+
+TableEnvironment env = TableEnvironment.create(...);
+
+// 在 Table API 里不经注册直接“内联”调用函数
+env.from("MyTable").select(call(HashFunction.class, $("myField")));
+
+// 注册函数
+env.createTemporarySystemFunction("HashFunction", HashFunction.class);
+
+// 在 Table API 里调用注册好的函数
+env.from("MyTable").select(call("HashFunction", $("myField")));
+
+// 在 SQL 里调用注册好的函数
+env.sqlQuery("SELECT HashFunction(myField) FROM MyTable");
{% endhighlight %}
+
+
+
+{% highlight scala %}
+import org.apache.flink.table.annotation.InputGroup
+import org.apache.flink.table.api._
+import org.apache.flink.table.functions.ScalarFunction
+
+class HashFunction extends ScalarFunction {
+
+ // 接受任意类型输入,返回 INT 型输出
+ def eval(@DataTypeHint(inputGroup = InputGroup.ANY) o: AnyRef): Int {
+ return o.hashCode();
+ }
+}
+
+val env = TableEnvironment.create(...)
+
+// 在 Table API 里不经注册直接“内联”调用函数
+env.from("MyTable").select(call(classOf[HashFunction], $"myField"))
+
+// 注册函数
+env.createTemporarySystemFunction("HashFunction", classOf[HashFunction])
-There are many ways to define a Python table function besides extending the base class `TableFunction`.
-Please refer to the [Python Table Function]({{ site.baseurl }}/zh/dev/table/python/python_udfs.html#table-functions) documentation for more details.
+// 在 Table API 里调用注册好的函数
+env.from("MyTable").select(call("HashFunction", $"myField"))
+// 在 SQL 里调用注册好的函数
+env.sqlQuery("SELECT HashFunction(myField) FROM MyTable")
+
+{% endhighlight %}
+
-
-
-Please note that POJO types do not have a deterministic field order. Therefore, you cannot rename the fields of POJO returned by a table function using `AS`.
+如果你打算使用 Python 实现或调用标量函数,详情可参考 [Python 标量函数]({% link dev/table/python/python_udfs.zh.md %}#scalar-functions)。
-By default the result type of a `TableFunction` is determined by Flink’s automatic type extraction facilities. This works well for basic types and simple POJOs but might be wrong for more complex, custom, or composite types. In such a case, the type of the result can be manually specified by overriding `TableFunction#getResultType()` which returns its `TypeInformation`.
+{% top %}
-The following example shows an example of a `TableFunction` that returns a `Row` type which requires explicit type information. We define that the returned table type should be `RowTypeInfo(String, Integer)` by overriding `TableFunction#getResultType()`.
+表值函数
+---------------
-{% highlight java %}
-public class CustomTypeSplit extends TableFunction {
- public void eval(String str) {
- for (String s : str.split(" ")) {
- Row row = new Row(2);
- row.setField(0, s);
- row.setField(1, s.length());
- collect(row);
- }
- }
+跟自定义标量函数一样,自定义表值函数的输入参数也可以是 0 到多个标量。但是跟标量函数只能返回一个值不同的是,它可以返回任意多行。返回的每一行可以包含 1 到多列,如果输出行只包含 1 列,会省略结构化信息并生成标量值,这个标量值在运行阶段会隐式地包装进行里。
- @Override
- public TypeInformation getResultType() {
- return Types.ROW(Types.STRING(), Types.INT());
+要定义一个表值函数,你需要扩展 `org.apache.flink.table.functions` 下的 `TableFunction`,可以通过实现多个名为 `eval` 的方法对求值方法进行重载。像其他函数一样,输入和输出类型也可以通过反射自动提取出来。表值函数返回的表的类型取决于 `TableFunction` 类的泛型参数 `T`,不同于标量函数,表值函数的求值方法本身不包含返回类型,而是通过 `collect(T)` 方法来发送要输出的行。
+
+在 Table API 中,表值函数是通过 `.joinLateral(...)` 或者 `.leftOuterJoinLateral(...)` 来使用的。`joinLateral` 算子会把外表(算子左侧的表)的每一行跟跟表值函数返回的所有行(位于算子右侧)进行 (cross)join。`leftOuterJoinLateral` 算子也是把外表(算子左侧的表)的每一行跟表值函数返回的所有行(位于算子右侧)进行(cross)join,并且如果表值函数返回 0 行也会保留外表的这一行。
+
+在 SQL 里面用 `JOIN` 或者 以 `ON TRUE` 为条件的 `LEFT JOIN` 来配合 `LATERAL TABLE()` 的使用。
+
+下面的例子展示了如何实现一个分隔函数并在查询里调用它,详情可参考[开发指南](#开发指南):
+
+
-Please note that POJO types do not have a deterministic field order. Therefore, you cannot rename the fields of POJO returned by a table function using `AS`.
-
-By default the result type of a `TableFunction` is determined by Flink’s automatic type extraction facilities. This works well for basic types and simple POJOs but might be wrong for more complex, custom, or composite types. In such a case, the type of the result can be manually specified by overriding `TableFunction#getResultType()` which returns its `TypeInformation`.
+
+{% highlight scala %}
+import org.apache.flink.table.annotation.DataTypeHint
+import org.apache.flink.table.annotation.FunctionHint
+import org.apache.flink.table.api._
+import org.apache.flink.table.functions.TableFunction
+import org.apache.flink.types.Row
-The following example shows an example of a `TableFunction` that returns a `Row` type which requires explicit type information. We define that the returned table type should be `RowTypeInfo(String, Integer)` by overriding `TableFunction#getResultType()`.
+@FunctionHint(output = @DataTypeHint("ROW"))
+class SplitFunction extends TableFunction[Row] {
-{% highlight scala %}
-class CustomTypeSplit extends TableFunction[Row] {
def eval(str: String): Unit = {
- str.split(" ").foreach({ s =>
- val row = new Row(2)
- row.setField(0, s)
- row.setField(1, s.length)
- collect(row)
- })
- }
-
- override def getResultType: TypeInformation[Row] = {
- Types.ROW(Types.STRING, Types.INT)
+ // use collect(...) to emit a row
+ str.split(" ").foreach(s => collect(Row.of(s, s.length)))
}
}
+
+val env = TableEnvironment.create(...)
+
+// 在 Table API 里不经注册直接“内联”调用函数
+env
+ .from("MyTable")
+ .joinLateral(call(classOf[SplitFunction], $"myField")
+ .select($"myField", $"word", $"length")
+env
+ .from("MyTable")
+ .leftOuterJoinLateral(call(classOf[SplitFunction], $"myField"))
+ .select($"myField", $"word", $"length")
+
+// 在 Table API 里重命名函数字段
+env
+ .from("MyTable")
+ .leftOuterJoinLateral(call(classOf[SplitFunction], $"myField").as("newWord", "newLength"))
+ .select($"myField", $"newWord", $"newLength")
+
+// 注册函数
+env.createTemporarySystemFunction("SplitFunction", classOf[SplitFunction])
+
+// 在 Table API 里调用注册好的函数
+env
+ .from("MyTable")
+ .joinLateral(call("SplitFunction", $"myField"))
+ .select($"myField", $"word", $"length")
+env
+ .from("MyTable")
+ .leftOuterJoinLateral(call("SplitFunction", $"myField"))
+ .select($"myField", $"word", $"length")
+
+// 在 SQL 里调用注册好的函数
+env.sqlQuery(
+ "SELECT myField, word, length " +
+ "FROM MyTable, LATERAL TABLE(SplitFunction(myField))");
+env.sqlQuery(
+ "SELECT myField, word, length " +
+ "FROM MyTable " +
+ "LEFT JOIN LATERAL TABLE(SplitFunction(myField)) ON TRUE")
+
+// 在 SQL 里重命名函数字段
+env.sqlQuery(
+ "SELECT myField, newWord, newLength " +
+ "FROM MyTable " +
+ "LEFT JOIN LATERAL TABLE(SplitFunction(myField)) AS T(newWord, newLength) ON TRUE")
+
{% endhighlight %}
+
-{% top %}
+如果你打算使用 Scala,不要把表值函数声明为 Scala `object`,Scala `object` 是单例对象,将导致并发问题。
+如果你打算使用 Python 实现或调用表值函数,详情可参考 [Python 表值函数]({% link dev/table/python/python_udfs.zh.md %}#table-functions)。
-Aggregation Functions
+{% top %}
+
+聚合函数
---------------------
-User-Defined Aggregate Functions (UDAGGs) aggregate a table (one or more rows with one or more attributes) to a scalar value.
+自定义聚合函数(UDAGG)是把一个表(一行或者多行,每行可以有一列或者多列)聚合成一个标量值。
-The above figure shows an example of an aggregation. Assume you have a table that contains data about beverages. The table consists of three columns, `id`, `name` and `price` and 5 rows. Imagine you need to find the highest price of all beverages in the table, i.e., perform a `max()` aggregation. You would need to check each of the 5 rows and the result would be a single numeric value.
+上面的图片展示了一个聚合的例子。假设你有一个关于饮料的表。表里面有三个字段,分别是 `id`、`name`、`price`,表里有 5 行数据。假设你需要找到所有饮料里最贵的饮料的价格,即执行一个 `max()` 聚合。你需要遍历所有 5 行数据,而结果就只有一个数值。
-User-defined aggregation functions are implemented by extending the `AggregateFunction` class. An `AggregateFunction` works as follows. First, it needs an `accumulator`, which is the data structure that holds the intermediate result of the aggregation. An empty accumulator is created by calling the `createAccumulator()` method of the `AggregateFunction`. Subsequently, the `accumulate()` method of the function is called for each input row to update the accumulator. Once all rows have been processed, the `getValue()` method of the function is called to compute and return the final result.
+自定义聚合函数是通过扩展 `AggregateFunction` 来实现的。`AggregateFunction` 的工作过程如下。首先,它需要一个 `accumulator`,它是一个数据结构,存储了聚合的中间结果。通过调用 `AggregateFunction` 的 `createAccumulator()` 方法创建一个空的 accumulator。接下来,对于每一行数据,会调用 `accumulate()` 方法来更新 accumulator。当所有的数据都处理完了之后,通过调用 `getValue` 方法来计算和返回最终的结果。
-**The following methods are mandatory for each `AggregateFunction`:**
+**下面几个方法是每个 `AggregateFunction` 必须要实现的:**
- `createAccumulator()`
-- `accumulate()`
+- `accumulate()`
- `getValue()`
-Flink’s type extraction facilities can fail to identify complex data types, e.g., if they are not basic types or simple POJOs. So similar to `ScalarFunction` and `TableFunction`, `AggregateFunction` provides methods to specify the `TypeInformation` of the result type (through
- `AggregateFunction#getResultType()`) and the type of the accumulator (through `AggregateFunction#getAccumulatorType()`).
-
-Besides the above methods, there are a few contracted methods that can be
-optionally implemented. While some of these methods allow the system more efficient query execution, others are mandatory for certain use cases. For instance, the `merge()` method is mandatory if the aggregation function should be applied in the context of a session group window (the accumulators of two session windows need to be joined when a row is observed that "connects" them).
+Flink 的类型推导在遇到复杂类型的时候可能会推导出错误的结果,比如那些非基本类型和普通的 POJO 类型的复杂类型。所以跟 `ScalarFunction` 和 `TableFunction` 一样,`AggregateFunction` 也提供了 `AggregateFunction#getResultType()` 和 `AggregateFunction#getAccumulatorType()` 来分别指定返回值类型和 accumulator 的类型,两个函数的返回值类型也都是 `TypeInformation`。
+
+除了上面的方法,还有几个方法可以选择实现。这些方法有些可以让查询更加高效,而有些是在某些特定场景下必须要实现的。例如,如果聚合函数用在会话窗口(当两个会话窗口合并的时候需要 merge 他们的 accumulator)的话,`merge()` 方法就是必须要实现的。
-**The following methods of `AggregateFunction` are required depending on the use case:**
+**`AggregateFunction` 的以下方法在某些场景下是必须实现的:**
-- `retract()` is required for aggregations on bounded `OVER` windows.
-- `merge()` is required for many batch aggregations and session window aggregations.
-- `resetAccumulator()` is required for many batch aggregations.
+- `retract()` 在 bounded `OVER` 窗口中是必须实现的。
+- `merge()` 在许多批式聚合和会话窗口聚合中是必须实现的。
+- `resetAccumulator()` 在许多批式聚合中是必须实现的。
-All methods of `AggregateFunction` must be declared as `public`, not `static` and named exactly as the names mentioned above. The methods `createAccumulator`, `getValue`, `getResultType`, and `getAccumulatorType` are defined in the `AggregateFunction` abstract class, while others are contracted methods. In order to define a aggregate function, one has to extend the base class `org.apache.flink.table.functions.AggregateFunction` and implement one (or more) `accumulate` methods. The method `accumulate` can be overloaded with different parameter types and supports variable arguments.
+`AggregateFunction` 的所有方法都必须是 `public` 的,不能是 `static` 的,而且名字必须跟上面写的一样。`createAccumulator`、`getValue`、`getResultType` 以及 `getAccumulatorType` 这几个函数是在抽象类 `AggregateFunction` 中定义的,而其他函数都是约定的方法。如果要定义一个聚合函数,你需要扩展 `org.apache.flink.table.functions.AggregateFunction`,并且实现一个(或者多个)`accumulate` 方法。`accumulate` 方法可以重载,每个方法的参数类型不同,并且支持变长参数。
-Detailed documentation for all methods of `AggregateFunction` is given below.
+`AggregateFunction` 的所有方法的详细文档如下。
@@ -418,7 +941,7 @@ public abstract class UserDefinedAggregateFunction extends UserDefinedFu
}
/**
- * Base class for aggregation functions.
+ * Base class for aggregation functions.
*
* @param the type of the aggregation result
* @param the type of the aggregation accumulator. The accumulator is used to keep the
@@ -527,7 +1050,7 @@ abstract class UserDefinedAggregateFunction[T, ACC] extends UserDefinedFunction
}
/**
- * Base class for aggregation functions.
+ * Base class for aggregation functions.
*
* @tparam T the type of the aggregation result
* @tparam ACC the type of the aggregation accumulator. The accumulator is used to keep the
@@ -570,7 +1093,7 @@ abstract class AggregateFunction[T, ACC] extends UserDefinedAggregateFunction[T,
* merged.
*/
def merge(accumulator: ACC, its: java.lang.Iterable[ACC]): Unit // OPTIONAL
-
+
/**
* Called every time when an aggregation result should be materialized.
* The returned value could be either an early and incomplete result
@@ -603,15 +1126,15 @@ abstract class AggregateFunction[T, ACC] extends UserDefinedAggregateFunction[T,
-The following example shows how to
+下面的例子展示了如何:
-- define an `AggregateFunction` that calculates the weighted average on a given column,
-- register the function in the `TableEnvironment`, and
-- use the function in a query.
+- 定义一个聚合函数来计算某一列的加权平均,
+- 在 `TableEnvironment` 中注册函数,
+- 在查询中使用函数。
-To calculate an weighted average value, the accumulator needs to store the weighted sum and count of all the data that has been accumulated. In our example we define a class `WeightedAvgAccum` to be the accumulator. Accumulators are automatically backup-ed by Flink's checkpointing mechanism and restored in case of a failure to ensure exactly-once semantics.
+为了计算加权平均值,accumulator 需要存储加权总和以及数据的条数。在我们的例子里,我们定义了一个类 `WeightedAvgAccum` 来作为 accumulator。Flink 的 checkpoint 机制会自动保存 accumulator,在失败时进行恢复,以此来保证精确一次的语义。
-The `accumulate()` method of our `WeightedAvg` `AggregateFunction` has three inputs. The first one is the `WeightedAvgAccum` accumulator, the other two are user-defined inputs: input value `ivalue` and weight of the input `iweight`. Although the `retract()`, `merge()`, and `resetAccumulator()` methods are not mandatory for most aggregation types, we provide them below as examples. Please note that we used Java primitive types and defined `getResultType()` and `getAccumulatorType()` methods in the Scala example because Flink type extraction does not work very well for Scala types.
+我们的 `WeightedAvg`(聚合函数)的 `accumulate` 方法有三个输入参数。第一个是 `WeightedAvgAccum` accumulator,另外两个是用户自定义的输入:输入的值 `ivalue` 和 输入的权重 `iweight`。尽管 `retract()`、`merge()`、`resetAccumulator()` 这几个方法在大多数聚合类型中都不是必须实现的,我们也在样例中提供了他们的实现。请注意我们在 Scala 样例中也是用的是 Java 的基础类型,并且定义了 `getResultType()` 和 `getAccumulatorType()`,因为 Flink 的类型推导对于 Scala 的类型推导做的不是很好。
@@ -652,7 +1175,7 @@ public static class WeightedAvg extends AggregateFunction it) {
Iterator iter = it.iterator();
while (iter.hasNext()) {
@@ -661,18 +1184,18 @@ public static class WeightedAvg extends AggregateFunction it) {
Iterator iter = it.iterator();
while (iter.hasNext()) {
@@ -804,7 +1328,7 @@ public static class WeightedAvg extends AggregateFunction
-
{% top %}
-Table Aggregation Functions
+表值聚合函数
---------------------
-User-Defined Table Aggregate Functions (UDTAGGs) aggregate a table (one or more rows with one or more attributes) to a result table with multi rows and columns.
+自定义表值聚合函数(UDTAGG)可以把一个表(一行或者多行,每行有一列或者多列)聚合成另一张表,结果中可以有多行多列。
-The above figure shows an example of a table aggregation. Assume you have a table that contains data about beverages. The table consists of three columns, `id`, `name` and `price` and 5 rows. Imagine you need to find the top 2 highest prices of all beverages in the table, i.e., perform a `top2()` table aggregation. You would need to check each of the 5 rows and the result would be a table with the top 2 values.
+上图展示了一个表值聚合函数的例子。假设你有一个饮料的表,这个表有 3 列,分别是 `id`、`name` 和 `price`,一共有 5 行。假设你需要找到价格最高的两个饮料,类似于 `top2()` 表值聚合函数。你需要遍历所有 5 行数据,结果是有 2 行数据的一个表。
-User-defined table aggregation functions are implemented by extending the `TableAggregateFunction` class. A `TableAggregateFunction` works as follows. First, it needs an `accumulator`, which is the data structure that holds the intermediate result of the aggregation. An empty accumulator is created by calling the `createAccumulator()` method of the `TableAggregateFunction`. Subsequently, the `accumulate()` method of the function is called for each input row to update the accumulator. Once all rows have been processed, the `emitValue()` method of the function is called to compute and return the final results.
+用户自定义表值聚合函数是通过扩展 `TableAggregateFunction` 类来实现的。一个 `TableAggregateFunction` 的工作过程如下。首先,它需要一个 `accumulator`,这个 `accumulator` 负责存储聚合的中间结果。 通过调用 `TableAggregateFunction` 的 `createAccumulator` 方法来构造一个空的 accumulator。接下来,对于每一行数据,会调用 `accumulate` 方法来更新 accumulator。当所有数据都处理完之后,调用 `emitValue` 方法来计算和返回最终的结果。
-**The following methods are mandatory for each `TableAggregateFunction`:**
+**下面几个 `TableAggregateFunction` 的方法是必须要实现的:**
- `createAccumulator()`
-- `accumulate()`
+- `accumulate()`
+
+Flink 的类型推导在遇到复杂类型的时候可能会推导出错误的结果,比如那些非基本类型和普通的 POJO 类型的复杂类型。所以类似于 `ScalarFunction` 和 `TableFunction`,`TableAggregateFunction` 也提供了 `TableAggregateFunction#getResultType()` 和 `TableAggregateFunction#getAccumulatorType()` 方法来指定返回值类型和 accumulator 的类型,这两个方法都需要返回 `TypeInformation`。
-Flink’s type extraction facilities can fail to identify complex data types, e.g., if they are not basic types or simple POJOs. So similar to `ScalarFunction` and `TableFunction`, `TableAggregateFunction` provides methods to specify the `TypeInformation` of the result type (through
- `TableAggregateFunction#getResultType()`) and the type of the accumulator (through `TableAggregateFunction#getAccumulatorType()`).
-
-Besides the above methods, there are a few contracted methods that can be
-optionally implemented. While some of these methods allow the system more efficient query execution, others are mandatory for certain use cases. For instance, the `merge()` method is mandatory if the aggregation function should be applied in the context of a session group window (the accumulators of two session windows need to be joined when a row is observed that "connects" them).
+除了上面的方法,还有几个其他的方法可以选择性的实现。有些方法可以让查询更加高效,而有些方法对于某些特定场景是必须要实现的。比如,在会话窗口(当两个会话窗口合并时会合并两个 accumulator)中使用聚合函数时,必须要实现`merge()` 方法。
-**The following methods of `TableAggregateFunction` are required depending on the use case:**
+**下面几个 `TableAggregateFunction` 的方法在某些特定场景下是必须要实现的:**
-- `retract()` is required for aggregations on bounded `OVER` windows.
-- `merge()` is required for many batch aggregations and session window aggregations.
-- `resetAccumulator()` is required for many batch aggregations.
-- `emitValue()` is required for batch and window aggregations.
+- `retract()` 在 bounded `OVER` 窗口中的聚合函数必须要实现。
+- `merge()` 在许多批式聚合和会话窗口聚合中是必须要实现的。
+- `resetAccumulator()` 在许多批式聚合中是必须要实现的。
+- `emitValue()` 在批式聚合以及窗口聚合中是必须要实现的。
-**The following methods of `TableAggregateFunction` are used to improve the performance of streaming jobs:**
+**下面的 `TableAggregateFunction` 的方法可以提升流式任务的效率:**
-- `emitUpdateWithRetract()` is used to emit values that have been updated under retract mode.
+- `emitUpdateWithRetract()` 在 retract 模式下,该方法负责发送被更新的值。
-For `emitValue` method, it emits full data according to the accumulator. Take TopN as an example, `emitValue` emit all top n values each time. This may bring performance problems for streaming jobs. To improve the performance, a user can also implement `emitUpdateWithRetract` method to improve the performance. The method outputs data incrementally in retract mode, i.e., once there is an update, we have to retract old records before sending new updated ones. The method will be used in preference to the `emitValue` method if they are all defined in the table aggregate function, because `emitUpdateWithRetract` is treated to be more efficient than `emitValue` as it can output values incrementally.
+`emitValue` 方法会发送所有 accumulator 给出的结果。拿 TopN 来说,`emitValue` 每次都会发送所有的最大的 n 个值。这在流式任务中可能会有一些性能问题。为了提升性能,用户可以实现 `emitUpdateWithRetract` 方法。这个方法在 retract 模式下会增量的输出结果,比如有数据更新了,我们必须要撤回老的数据,然后再发送新的数据。如果定义了 `emitUpdateWithRetract` 方法,那它会优先于 `emitValue` 方法被使用,因为一般认为 `emitUpdateWithRetract` 会更加高效,因为它的输出是增量的。
-All methods of `TableAggregateFunction` must be declared as `public`, not `static` and named exactly as the names mentioned above. The methods `createAccumulator`, `getResultType`, and `getAccumulatorType` are defined in the parent abstract class of `TableAggregateFunction`, while others are contracted methods. In order to define a table aggregate function, one has to extend the base class `org.apache.flink.table.functions.TableAggregateFunction` and implement one (or more) `accumulate` methods. The method `accumulate` can be overloaded with different parameter types and supports variable arguments.
+`TableAggregateFunction` 的所有方法都必须是 `public` 的、非 `static` 的,而且名字必须跟上面提到的一样。`createAccumulator`、`getResultType` 和 `getAccumulatorType` 这三个方法是在抽象父类 `TableAggregateFunction` 中定义的,而其他的方法都是约定的方法。要实现一个表值聚合函数,你必须扩展 `org.apache.flink.table.functions.TableAggregateFunction`,并且实现一个(或者多个)`accumulate` 方法。`accumulate` 方法可以有多个重载的方法,也可以支持变长参数。
-Detailed documentation for all methods of `TableAggregateFunction` is given below.
+`TableAggregateFunction` 的所有方法的详细文档如下。
@@ -905,7 +1426,7 @@ public abstract class UserDefinedAggregateFunction extends UserDefinedFu
}
/**
- * Base class for table aggregation functions.
+ * Base class for table aggregation functions.
*
* @param the type of the aggregation result
* @param the type of the aggregation accumulator. The accumulator is used to keep the
@@ -958,7 +1479,7 @@ public abstract class TableAggregateFunction extends UserDefinedAggregat
* @param out the collector used to output data
*/
public void emitValue(ACC accumulator, Collector out); // OPTIONAL
-
+
/**
* Called every time when an aggregation result should be materialized. The returned value
* could be either an early and incomplete result (periodically emitted as data arrive) or
@@ -978,7 +1499,7 @@ public abstract class TableAggregateFunction extends UserDefinedAggregat
* records.
*/
public void emitUpdateWithRetract(ACC accumulator, RetractableCollector out); // OPTIONAL
-
+
/**
* Collects a record and forwards it. The collector can output retract messages with the retract
* method. Note: only use it in {@code emitRetractValueIncrementally}.
@@ -1032,7 +1553,7 @@ abstract class UserDefinedAggregateFunction[T, ACC] extends UserDefinedFunction
}
/**
- * Base class for table aggregation functions.
+ * Base class for table aggregation functions.
*
* @tparam T the type of the aggregation result
* @tparam ACC the type of the aggregation accumulator. The accumulator is used to keep the
@@ -1075,7 +1596,7 @@ abstract class TableAggregateFunction[T, ACC] extends UserDefinedAggregateFuncti
* merged.
*/
def merge(accumulator: ACC, its: java.lang.Iterable[ACC]): Unit // OPTIONAL
-
+
/**
* Called every time when an aggregation result should be materialized. The returned value
* could be either an early and incomplete result (periodically emitted as data arrive) or
@@ -1106,13 +1627,13 @@ abstract class TableAggregateFunction[T, ACC] extends UserDefinedAggregateFuncti
* records.
*/
def emitUpdateWithRetract(accumulator: ACC, out: RetractableCollector[T]): Unit // OPTIONAL
-
+
/**
* Collects a record and forwards it. The collector can output retract messages with the retract
* method. Note: only use it in `emitRetractValueIncrementally`.
*/
trait RetractableCollector[T] extends Collector[T] {
-
+
/**
* Retract a record.
*
@@ -1126,15 +1647,15 @@ abstract class TableAggregateFunction[T, ACC] extends UserDefinedAggregateFuncti
-The following example shows how to
+下面的例子展示了如何
-- define a `TableAggregateFunction` that calculates the top 2 values on a given column,
-- register the function in the `TableEnvironment`, and
-- use the function in a Table API query(TableAggregateFunction is only supported by Table API).
+- 定义一个 `TableAggregateFunction` 来计算给定列的最大的 2 个值,
+- 在 `TableEnvironment` 中注册函数,
+- 在 Table API 查询中使用函数(当前只在 Table API 中支持 TableAggregateFunction)。
-To calculate the top 2 values, the accumulator needs to store the biggest 2 values of all the data that has been accumulated. In our example we define a class `Top2Accum` to be the accumulator. Accumulators are automatically backup-ed by Flink's checkpointing mechanism and restored in case of a failure to ensure exactly-once semantics.
+为了计算最大的 2 个值,accumulator 需要保存当前看到的最大的 2 个值。在我们的例子中,我们定义了类 `Top2Accum` 来作为 accumulator。Flink 的 checkpoint 机制会自动保存 accumulator,并且在失败时进行恢复,来保证精确一次的语义。
-The `accumulate()` method of our `Top2` `TableAggregateFunction` has two inputs. The first one is the `Top2Accum` accumulator, the other one is the user-defined input: input value `v`. Although the `merge()` method is not mandatory for most table aggregation types, we provide it below as examples. Please note that we used Java primitive types and defined `getResultType()` and `getAccumulatorType()` methods in the Scala example because Flink type extraction does not work very well for Scala types.
+我们的 `Top2` 表值聚合函数(`TableAggregateFunction`)的 `accumulate()` 方法有两个输入,第一个是 `Top2Accum` accumulator,另一个是用户定义的输入:输入的值 `v`。尽管 `merge()` 方法在大多数聚合类型中不是必须的,我们也在样例中提供了它的实现。请注意,我们在 Scala 样例中也使用的是 Java 的基础类型,并且定义了 `getResultType()` 和 `getAccumulatorType()` 方法,因为 Flink 的类型推导对于 Scala 的类型推导支持的不是很好。
@@ -1188,14 +1709,14 @@ public static class Top2 extends TableAggregateFunction
}
}
-// register function
+// 注册函数
StreamTableEnvironment tEnv = ...
tEnv.registerFunction("top2", new Top2());
-// init table
+// 初始化表
Table tab = ...;
-// use function
+// 使用函数
tab.groupBy("key")
.flatAggregate("top2(a) as (v, rank)")
.select("key, v, rank");
@@ -1258,10 +1779,10 @@ class Top2 extends TableAggregateFunction[JTuple2[JInteger, JInteger], Top2Accum
}
}
-// init table
+// 初始化表
val tab = ...
-// use function
+// 使用函数
tab
.groupBy('key)
.flatAggregate(top2('a) as ('v, 'rank))
@@ -1272,7 +1793,7 @@ tab
-The following example shows how to use `emitUpdateWithRetract` method to emit only updates. To emit only updates, in our example, the accumulator keeps both old and new top 2 values. Note: if the N of topN is big, it may inefficient to keep both old and new values. One way to solve this case is to store the input record into the accumulator in `accumulate` method and then perform calculation in `emitUpdateWithRetract`.
+下面的例子展示了如何使用 `emitUpdateWithRetract` 方法来只发送更新的数据。为了只发送更新的结果,accumulator 保存了上一次的最大的2个值,也保存了当前最大的2个值。注意:如果 TopN 中的 n 非常大,这种既保存上次的结果,也保存当前的结果的方式不太高效。一种解决这种问题的方式是把输入数据直接存储到 `accumulator` 中,然后在调用 `emitUpdateWithRetract` 方法时再进行计算。
@@ -1332,14 +1853,14 @@ public static class Top2 extends TableAggregateFunction
}
}
-// register function
+// 注册函数
StreamTableEnvironment tEnv = ...
tEnv.registerFunction("top2", new Top2());
-// init table
+// 初始化表
Table tab = ...;
-// use function
+// 使用函数
tab.groupBy("key")
.flatAggregate("top2(a) as (v, rank)")
.select("key, v, rank");
@@ -1409,10 +1930,10 @@ class Top2 extends TableAggregateFunction[JTuple2[JInteger, JInteger], Top2Accum
}
}
-// init table
+// 初始化表
val tab = ...
-// use function
+// 使用函数
tab
.groupBy('key)
.flatAggregate(top2('a) as ('v, 'rank))
@@ -1422,104 +1943,4 @@ tab
-
-{% top %}
-
-Best Practices for Implementing UDFs
-------------------------------------
-
-The Table API and SQL code generation internally tries to work with primitive values as much as possible. A user-defined function can introduce much overhead through object creation, casting, and (un)boxing. Therefore, it is highly recommended to declare parameters and result types as primitive types instead of their boxed classes. `Types.DATE` and `Types.TIME` can also be represented as `int`. `Types.TIMESTAMP` can be represented as `long`.
-
-We recommended that user-defined functions should be written by Java instead of Scala as Scala types pose a challenge for Flink's type extractor.
-
{% top %}
-
-Integrating UDFs with the Runtime
----------------------------------
-
-Sometimes it might be necessary for a user-defined function to get global runtime information or do some setup/clean-up work before the actual work. User-defined functions provide `open()` and `close()` methods that can be overridden and provide similar functionality as the methods in `RichFunction` of DataSet or DataStream API.
-
-The `open()` method is called once before the evaluation method. The `close()` method after the last call to the evaluation method.
-
-The `open()` method provides a `FunctionContext` that contains information about the context in which user-defined functions are executed, such as the metric group, the distributed cache files, or the global job parameters.
-
-The following information can be obtained by calling the corresponding methods of `FunctionContext`:
-
-| Method | Description |
-| :------------------------------------ | :----------------------------------------------------- |
-| `getMetricGroup()` | Metric group for this parallel subtask. |
-| `getCachedFile(name)` | Local temporary file copy of a distributed cache file. |
-| `getJobParameter(name, defaultValue)` | Global job parameter value associated with given key. |
-
-The following example snippet shows how to use `FunctionContext` in a scalar function for accessing a global job parameter:
-
-
-
-{% highlight java %}
-public class HashCode extends ScalarFunction {
-
- private int factor = 0;
-
- @Override
- public void open(FunctionContext context) throws Exception {
- // access "hashcode_factor" parameter
- // "12" would be the default value if parameter does not exist
- factor = Integer.valueOf(context.getJobParameter("hashcode_factor", "12"));
- }
-
- public int eval(String s) {
- return s.hashCode() * factor;
- }
-}
-
-ExecutionEnvironment env = ExecutionEnvironment.getExecutionEnvironment();
-BatchTableEnvironment tableEnv = BatchTableEnvironment.create(env);
-
-// set job parameter
-Configuration conf = new Configuration();
-conf.setString("hashcode_factor", "31");
-env.getConfig().setGlobalJobParameters(conf);
-
-// register the function
-tableEnv.registerFunction("hashCode", new HashCode());
-
-// use the function in Java Table API
-myTable.select("string, string.hashCode(), hashCode(string)");
-
-// use the function in SQL
-tableEnv.sqlQuery("SELECT string, HASHCODE(string) FROM MyTable");
-{% endhighlight %}
-
-
-
-{% highlight scala %}
-object hashCode extends ScalarFunction {
-
- var hashcode_factor = 12
-
- override def open(context: FunctionContext): Unit = {
- // access "hashcode_factor" parameter
- // "12" would be the default value if parameter does not exist
- hashcode_factor = context.getJobParameter("hashcode_factor", "12").toInt
- }
-
- def eval(s: String): Int = {
- s.hashCode() * hashcode_factor
- }
-}
-
-val tableEnv = BatchTableEnvironment.create(env)
-
-// use the function in Scala Table API
-myTable.select('string, hashCode('string))
-
-// register and use the function in SQL
-tableEnv.registerFunction("hashCode", hashCode)
-tableEnv.sqlQuery("SELECT string, HASHCODE(string) FROM MyTable")
-{% endhighlight %}
-
-
-
-
-{% top %}
-
diff --git a/docs/dev/table/hive/hive_catalog.md b/docs/dev/table/hive/hive_catalog.md
index d907703fe24b7e..7b59d9aff73d40 100644
--- a/docs/dev/table/hive/hive_catalog.md
+++ b/docs/dev/table/hive/hive_catalog.md
@@ -387,3 +387,7 @@ Something to note about the type mapping:
* Hive's `TIMESTAMP` always has precision 9 and doesn't support other precisions. Hive UDFs, on the other hand, can process `TIMESTAMP` values with a precision <= 9.
* Hive doesn't support Flink's `TIMESTAMP_WITH_TIME_ZONE`, `TIMESTAMP_WITH_LOCAL_TIME_ZONE`, and `MULTISET`
* Flink's `INTERVAL` type cannot be mapped to Hive `INTERVAL` type yet
+
+## Scala Shell
+
+NOTE: since blink planner is not well supported in Scala Shell at the moment, it's **NOT** recommended to use Hive connector in Scala Shell.
diff --git a/docs/dev/table/hive/hive_catalog.zh.md b/docs/dev/table/hive/hive_catalog.zh.md
index d907703fe24b7e..05479b9a035eca 100644
--- a/docs/dev/table/hive/hive_catalog.zh.md
+++ b/docs/dev/table/hive/hive_catalog.zh.md
@@ -387,3 +387,7 @@ Something to note about the type mapping:
* Hive's `TIMESTAMP` always has precision 9 and doesn't support other precisions. Hive UDFs, on the other hand, can process `TIMESTAMP` values with a precision <= 9.
* Hive doesn't support Flink's `TIMESTAMP_WITH_TIME_ZONE`, `TIMESTAMP_WITH_LOCAL_TIME_ZONE`, and `MULTISET`
* Flink's `INTERVAL` type cannot be mapped to Hive `INTERVAL` type yet
+
+## Scala Shell
+
+注意:目前 blink planner 还不能很好的支持 Scala Shell,因此 **不** 建议在 Scala Shell 中使用 Hive 连接器。
diff --git a/docs/dev/table/hive/hive_dialect.md b/docs/dev/table/hive/hive_dialect.md
new file mode 100644
index 00000000000000..c5c039ca226cbe
--- /dev/null
+++ b/docs/dev/table/hive/hive_dialect.md
@@ -0,0 +1,350 @@
+---
+title: "Hive Dialect"
+nav-parent_id: hive_tableapi
+nav-pos: 1
+---
+
+
+Starting from 1.11.0, Flink allows users to write SQL statements in Hive syntax when Hive dialect
+is used. By providing compatibility with Hive syntax, we aim to improve the interoperability with
+Hive and reduce the scenarios when users need to switch between Flink and Hive in order to execute
+different statements.
+
+* This will be replaced by the TOC
+{:toc}
+
+## Use Hive Dialect
+
+Flink currently supports two SQL dialects: `default` and `hive`. You need to switch to Hive dialect
+before you can write in Hive syntax. The following describes how to set dialect with
+SQL Client and Table API. Also notice that you can dynamically switch dialect for each
+statement you execute. There's no need to restart a session to use a different dialect.
+
+### SQL Client
+
+SQL dialect can be specified via the `table.sql-dialect` property. Therefore you can set the initial dialect to use in
+the `configuration` section of the yaml file for your SQL Client.
+
+{% highlight yaml %}
+
+execution:
+ planner: blink
+ type: batch
+ result-mode: table
+
+configuration:
+ table.sql-dialect: hive
+
+{% endhighlight %}
+
+You can also set the dialect after the SQL Client has launched.
+
+{% highlight bash %}
+
+Flink SQL> set table.sql-dialect=hive; -- to use hive dialect
+[INFO] Session property has been set.
+
+Flink SQL> set table.sql-dialect=default; -- to use default dialect
+[INFO] Session property has been set.
+
+{% endhighlight %}
+
+### Table API
+
+You can set dialect for your TableEnvironment with Table API.
+
+{% highlight java %}
+
+EnvironmentSettings settings = EnvironmentSettings.newInstance().useBlinkPlanner()...build();
+TableEnvironment tableEnv = TableEnvironment.create(settings);
+// to use hive dialect
+tableEnv.getConfig().setSqlDialect(SqlDialect.HIVE);
+// to use default dialect
+tableEnv.getConfig().setSqlDialect(SqlDialect.DEFAULT);
+
+{% endhighlight %}
+
+## DDL
+
+This section lists the supported DDLs with the Hive dialect. We'll mainly focus on the syntax
+here. You can refer to [Hive doc](https://cwiki.apache.org/confluence/display/Hive/LanguageManual+DDL)
+for the semantics of each DDL statement.
+
+### DATABASE
+
+#### Show
+
+{% highlight sql %}
+SHOW DATABASES;
+{% endhighlight %}
+
+#### Create
+
+{% highlight sql %}
+CREATE (DATABASE|SCHEMA) [IF NOT EXISTS] database_name
+ [COMMENT database_comment]
+ [LOCATION fs_path]
+ [WITH DBPROPERTIES (property_name=property_value, ...)];
+{% endhighlight %}
+
+#### Alter
+
+##### Update Properties
+
+{% highlight sql %}
+ALTER (DATABASE|SCHEMA) database_name SET DBPROPERTIES (property_name=property_value, ...);
+{% endhighlight %}
+
+##### Update Owner
+
+{% highlight sql %}
+ALTER (DATABASE|SCHEMA) database_name SET OWNER [USER|ROLE] user_or_role;
+{% endhighlight %}
+
+##### Update Location
+
+{% highlight sql %}
+ALTER (DATABASE|SCHEMA) database_name SET LOCATION fs_path;
+{% endhighlight %}
+
+#### Drop
+
+{% highlight sql %}
+DROP (DATABASE|SCHEMA) [IF EXISTS] database_name [RESTRICT|CASCADE];
+{% endhighlight %}
+
+#### Use
+
+{% highlight sql %}
+USE database_name;
+{% endhighlight %}
+
+### TABLE
+
+#### Show
+
+{% highlight sql %}
+SHOW TABLES;
+{% endhighlight %}
+
+#### Create
+
+{% highlight sql %}
+CREATE [EXTERNAL] TABLE [IF NOT EXISTS] table_name
+ [(col_name data_type [column_constraint] [COMMENT col_comment], ... [table_constraint])]
+ [COMMENT table_comment]
+ [PARTITIONED BY (col_name data_type [COMMENT col_comment], ...)]
+ [
+ [ROW FORMAT row_format]
+ [STORED AS file_format]
+ ]
+ [LOCATION fs_path]
+ [TBLPROPERTIES (property_name=property_value, ...)]
+
+row_format:
+ : DELIMITED [FIELDS TERMINATED BY char [ESCAPED BY char]] [COLLECTION ITEMS TERMINATED BY char]
+ [MAP KEYS TERMINATED BY char] [LINES TERMINATED BY char]
+ [NULL DEFINED AS char]
+ | SERDE serde_name [WITH SERDEPROPERTIES (property_name=property_value, ...)]
+
+file_format:
+ : SEQUENCEFILE
+ | TEXTFILE
+ | RCFILE
+ | ORC
+ | PARQUET
+ | AVRO
+ | INPUTFORMAT input_format_classname OUTPUTFORMAT output_format_classname
+
+column_constraint:
+ : NOT NULL [[ENABLE|DISABLE] [VALIDATE|NOVALIDATE] [RELY|NORELY]]
+
+table_constraint:
+ : [CONSTRAINT constraint_name] PRIMARY KEY (col_name, ...) [[ENABLE|DISABLE] [VALIDATE|NOVALIDATE] [RELY|NORELY]]
+{% endhighlight %}
+
+#### Alter
+
+##### Rename
+
+{% highlight sql %}
+ALTER TABLE table_name RENAME TO new_table_name;
+{% endhighlight %}
+
+##### Update Properties
+
+{% highlight sql %}
+ALTER TABLE table_name SET TBLPROPERTIES (property_name = property_value, property_name = property_value, ... );
+{% endhighlight %}
+
+##### Update Location
+
+{% highlight sql %}
+ALTER TABLE table_name [PARTITION partition_spec] SET LOCATION fs_path;
+{% endhighlight %}
+
+The `partition_spec`, if present, needs to be a full spec, i.e. has values for all partition columns. And when it's
+present, the operation will be applied to the corresponding partition instead of the table.
+
+##### Update File Format
+
+{% highlight sql %}
+ALTER TABLE table_name [PARTITION partition_spec] SET FILEFORMAT file_format;
+{% endhighlight %}
+
+The `partition_spec`, if present, needs to be a full spec, i.e. has values for all partition columns. And when it's
+present, the operation will be applied to the corresponding partition instead of the table.
+
+##### Update SerDe Properties
+
+{% highlight sql %}
+ALTER TABLE table_name [PARTITION partition_spec] SET SERDE serde_class_name [WITH SERDEPROPERTIES serde_properties];
+
+ALTER TABLE table_name [PARTITION partition_spec] SET SERDEPROPERTIES serde_properties;
+
+serde_properties:
+ : (property_name = property_value, property_name = property_value, ... )
+{% endhighlight %}
+
+The `partition_spec`, if present, needs to be a full spec, i.e. has values for all partition columns. And when it's
+present, the operation will be applied to the corresponding partition instead of the table.
+
+##### Add Partitions
+
+{% highlight sql %}
+ALTER TABLE table_name ADD [IF NOT EXISTS] (PARTITION partition_spec [LOCATION fs_path])+;
+{% endhighlight %}
+
+##### Drop Partitions
+
+{% highlight sql %}
+ALTER TABLE table_name DROP [IF EXISTS] PARTITION partition_spec[, PARTITION partition_spec, ...];
+{% endhighlight %}
+
+##### Add/Replace Columns
+
+{% highlight sql %}
+ALTER TABLE table_name
+ ADD|REPLACE COLUMNS (col_name data_type [COMMENT col_comment], ...)
+ [CASCADE|RESTRICT]
+{% endhighlight %}
+
+##### Change Column
+
+{% highlight sql %}
+ALTER TABLE table_name CHANGE [COLUMN] col_old_name col_new_name column_type
+ [COMMENT col_comment] [FIRST|AFTER column_name] [CASCADE|RESTRICT];
+{% endhighlight %}
+
+#### Drop
+
+{% highlight sql %}
+DROP TABLE [IF EXISTS] table_name;
+{% endhighlight %}
+
+### VIEW
+
+#### Create
+
+{% highlight sql %}
+CREATE VIEW [IF NOT EXISTS] view_name [(column_name, ...) ]
+ [COMMENT view_comment]
+ [TBLPROPERTIES (property_name = property_value, ...)]
+ AS SELECT ...;
+{% endhighlight %}
+
+#### Alter
+
+**NOTE**: Altering view only works in Table API, but not supported via SQL client.
+
+##### Rename
+
+{% highlight sql %}
+ALTER VIEW view_name RENAME TO new_view_name;
+{% endhighlight %}
+
+##### Update Properties
+
+{% highlight sql %}
+ALTER VIEW view_name SET TBLPROPERTIES (property_name = property_value, ... );
+{% endhighlight %}
+
+##### Update As Select
+
+{% highlight sql %}
+ALTER VIEW view_name AS select_statement;
+{% endhighlight %}
+
+#### Drop
+
+{% highlight sql %}
+DROP VIEW [IF EXISTS] view_name;
+{% endhighlight %}
+
+### FUNCTION
+
+#### Show
+
+{% highlight sql %}
+SHOW FUNCTIONS;
+{% endhighlight %}
+
+#### Create
+
+{% highlight sql %}
+CREATE FUNCTION function_name AS class_name;
+{% endhighlight %}
+
+#### Drop
+
+{% highlight sql %}
+DROP FUNCTION [IF EXISTS] function_name;
+{% endhighlight %}
+
+## DML
+
+### INSERT
+
+{% highlight sql %}
+INSERT (INTO|OVERWRITE) [TABLE] table_name [PARTITION partition_spec] SELECT ...;
+{% endhighlight %}
+
+The `partition_spec`, if present, can be either a full spec or partial spec. If the `partition_spec` is a partial
+spec, the dynamic partition column names can be omitted.
+
+## DQL
+
+At the moment, Hive dialect supports the same syntax as Flink SQL for DQLs. Refer to
+[Flink SQL queries]({{ site.baseurl }}/dev/table/sql/queries.html) for more details. And it's recommended to switch to
+`default` dialect to execute DQLs.
+
+## Notice
+
+The following are some precautions for using the Hive dialect.
+
+- Hive dialect should only be used to manipulate Hive tables, not generic tables. And Hive dialect should be used together
+with a [HiveCatalog]({{ site.baseurl }}/dev/table/hive/hive_catalog.html).
+- While all Hive versions support the same syntax, whether a specific feature is available still depends on the
+[Hive version]({{ site.baseurl }}/dev/table/hive/#supported-hive-versions) you use. For example, updating database
+location is only supported in Hive-2.4.0 or later.
+- Hive and Calcite have different sets of reserved keywords. For example, `default` is a reserved keyword in Calcite and
+a non-reserved keyword in Hive. Even with Hive dialect, you have to quote such keywords with backtick ( ` ) in order to
+use them as identifiers.
+- Due to expanded query incompatibility, views created in Flink cannot be queried in Hive.
diff --git a/docs/dev/table/hive/hive_dialect.zh.md b/docs/dev/table/hive/hive_dialect.zh.md
new file mode 100644
index 00000000000000..1236ad8324fef3
--- /dev/null
+++ b/docs/dev/table/hive/hive_dialect.zh.md
@@ -0,0 +1,350 @@
+---
+title: "Hive Dialect"
+nav-parent_id: hive_tableapi
+nav-pos: 1
+---
+
+
+Starting from 1.11.0, Flink allows users to write SQL statements in Hive syntax when Hive dialect
+is used. By providing compatibility with Hive syntax, we aim to improve the interoperability with
+Hive and reduce the scenarios when users need to switch between Flink and Hive in order to execute
+different statements.
+
+* This will be replaced by the TOC
+{:toc}
+
+## Use Hive Dialect
+
+Flink currently supports two SQL dialects: `default` and `hive`. You need to switch to Hive dialect
+before you can write in Hive syntax. The following describes how to set dialect with
+SQL Client and Table API. Also notice that you can dynamically switch dialect for each
+statement you execute. There's no need to restart a session to use a different dialect.
+
+### SQL Client
+
+SQL dialect can be specified via the `table.sql-dialect` property. Therefore you can set the initial dialect to use in
+the `configuration` section of the yaml file for your SQL Client.
+
+{% highlight yaml %}
+
+execution:
+ planner: blink
+ type: batch
+ result-mode: table
+
+configuration:
+ table.sql-dialect: hive
+
+{% endhighlight %}
+
+You can also set the dialect after the SQL Client has launched.
+
+{% highlight bash %}
+
+Flink SQL> set table.sql-dialect=hive; -- to use hive dialect
+[INFO] Session property has been set.
+
+Flink SQL> set table.sql-dialect=default; -- to use default dialect
+[INFO] Session property has been set.
+
+{% endhighlight %}
+
+### Table API
+
+You can set dialect for your TableEnvironment with Table API.
+
+{% highlight java %}
+
+EnvironmentSettings settings = EnvironmentSettings.newInstance().useBlinkPlanner()...build();
+TableEnvironment tableEnv = TableEnvironment.create(settings);
+// to use hive dialect
+tableEnv.getConfig().setSqlDialect(SqlDialect.HIVE);
+// to use default dialect
+tableEnv.getConfig().setSqlDialect(SqlDialect.DEFAULT);
+
+{% endhighlight %}
+
+## DDL
+
+This section lists the supported DDLs with the Hive dialect. We'll mainly focus on the syntax
+here. You can refer to [Hive doc](https://cwiki.apache.org/confluence/display/Hive/LanguageManual+DDL)
+for the semantics of each DDL statement.
+
+### DATABASE
+
+#### Show
+
+{% highlight sql %}
+SHOW DATABASES;
+{% endhighlight %}
+
+#### Create
+
+{% highlight sql %}
+CREATE (DATABASE|SCHEMA) [IF NOT EXISTS] database_name
+ [COMMENT database_comment]
+ [LOCATION fs_path]
+ [WITH DBPROPERTIES (property_name=property_value, ...)];
+{% endhighlight %}
+
+#### Alter
+
+##### Update Properties
+
+{% highlight sql %}
+ALTER (DATABASE|SCHEMA) database_name SET DBPROPERTIES (property_name=property_value, ...);
+{% endhighlight %}
+
+##### Update Owner
+
+{% highlight sql %}
+ALTER (DATABASE|SCHEMA) database_name SET OWNER [USER|ROLE] user_or_role;
+{% endhighlight %}
+
+##### Update Location
+
+{% highlight sql %}
+ALTER (DATABASE|SCHEMA) database_name SET LOCATION fs_path;
+{% endhighlight %}
+
+#### Drop
+
+{% highlight sql %}
+DROP (DATABASE|SCHEMA) [IF EXISTS] database_name [RESTRICT|CASCADE];
+{% endhighlight %}
+
+#### Use
+
+{% highlight sql %}
+USE database_name;
+{% endhighlight %}
+
+### TABLE
+
+#### Show
+
+{% highlight sql %}
+SHOW TABLES;
+{% endhighlight %}
+
+#### Create
+
+{% highlight sql %}
+CREATE [EXTERNAL] TABLE [IF NOT EXISTS] table_name
+ [(col_name data_type [column_constraint] [COMMENT col_comment], ... [table_constraint])]
+ [COMMENT table_comment]
+ [PARTITIONED BY (col_name data_type [COMMENT col_comment], ...)]
+ [
+ [ROW FORMAT row_format]
+ [STORED AS file_format]
+ ]
+ [LOCATION fs_path]
+ [TBLPROPERTIES (property_name=property_value, ...)]
+
+row_format:
+ : DELIMITED [FIELDS TERMINATED BY char [ESCAPED BY char]] [COLLECTION ITEMS TERMINATED BY char]
+ [MAP KEYS TERMINATED BY char] [LINES TERMINATED BY char]
+ [NULL DEFINED AS char]
+ | SERDE serde_name [WITH SERDEPROPERTIES (property_name=property_value, ...)]
+
+file_format:
+ : SEQUENCEFILE
+ | TEXTFILE
+ | RCFILE
+ | ORC
+ | PARQUET
+ | AVRO
+ | INPUTFORMAT input_format_classname OUTPUTFORMAT output_format_classname
+
+column_constraint:
+ : NOT NULL [[ENABLE|DISABLE] [VALIDATE|NOVALIDATE] [RELY|NORELY]]
+
+table_constraint:
+ : [CONSTRAINT constraint_name] PRIMARY KEY (col_name, ...) [[ENABLE|DISABLE] [VALIDATE|NOVALIDATE] [RELY|NORELY]]
+{% endhighlight %}
+
+#### Alter
+
+##### Rename
+
+{% highlight sql %}
+ALTER TABLE table_name RENAME TO new_table_name;
+{% endhighlight %}
+
+##### Update Properties
+
+{% highlight sql %}
+ALTER TABLE table_name SET TBLPROPERTIES (property_name = property_value, property_name = property_value, ... );
+{% endhighlight %}
+
+##### Update Location
+
+{% highlight sql %}
+ALTER TABLE table_name [PARTITION partition_spec] SET LOCATION fs_path;
+{% endhighlight %}
+
+The `partition_spec`, if present, needs to be a full spec, i.e. has values for all partition columns. And when it's
+present, the operation will be applied to the corresponding partition instead of the table.
+
+##### Update File Format
+
+{% highlight sql %}
+ALTER TABLE table_name [PARTITION partition_spec] SET FILEFORMAT file_format;
+{% endhighlight %}
+
+The `partition_spec`, if present, needs to be a full spec, i.e. has values for all partition columns. And when it's
+present, the operation will be applied to the corresponding partition instead of the table.
+
+##### Update SerDe Properties
+
+{% highlight sql %}
+ALTER TABLE table_name [PARTITION partition_spec] SET SERDE serde_class_name [WITH SERDEPROPERTIES serde_properties];
+
+ALTER TABLE table_name [PARTITION partition_spec] SET SERDEPROPERTIES serde_properties;
+
+serde_properties:
+ : (property_name = property_value, property_name = property_value, ... )
+{% endhighlight %}
+
+The `partition_spec`, if present, needs to be a full spec, i.e. has values for all partition columns. And when it's
+present, the operation will be applied to the corresponding partition instead of the table.
+
+##### Add Partitions
+
+{% highlight sql %}
+ALTER TABLE table_name ADD [IF NOT EXISTS] (PARTITION partition_spec [LOCATION fs_path])+;
+{% endhighlight %}
+
+##### Drop Partitions
+
+{% highlight sql %}
+ALTER TABLE table_name DROP [IF EXISTS] PARTITION partition_spec[, PARTITION partition_spec, ...];
+{% endhighlight %}
+
+##### Add/Replace Columns
+
+{% highlight sql %}
+ALTER TABLE table_name
+ ADD|REPLACE COLUMNS (col_name data_type [COMMENT col_comment], ...)
+ [CASCADE|RESTRICT]
+{% endhighlight %}
+
+##### Change Column
+
+{% highlight sql %}
+ALTER TABLE table_name CHANGE [COLUMN] col_old_name col_new_name column_type
+ [COMMENT col_comment] [FIRST|AFTER column_name] [CASCADE|RESTRICT];
+{% endhighlight %}
+
+#### Drop
+
+{% highlight sql %}
+DROP TABLE [IF EXISTS] table_name;
+{% endhighlight %}
+
+### VIEW
+
+#### Create
+
+{% highlight sql %}
+CREATE VIEW [IF NOT EXISTS] view_name [(column_name, ...) ]
+ [COMMENT view_comment]
+ [TBLPROPERTIES (property_name = property_value, ...)]
+ AS SELECT ...;
+{% endhighlight %}
+
+#### Alter
+
+**NOTE**: Altering view only works in Table API, but not supported via SQL client.
+
+##### Rename
+
+{% highlight sql %}
+ALTER VIEW view_name RENAME TO new_view_name;
+{% endhighlight %}
+
+##### Update Properties
+
+{% highlight sql %}
+ALTER VIEW view_name SET TBLPROPERTIES (property_name = property_value, ... );
+{% endhighlight %}
+
+##### Update As Select
+
+{% highlight sql %}
+ALTER VIEW view_name AS select_statement;
+{% endhighlight %}
+
+#### Drop
+
+{% highlight sql %}
+DROP VIEW [IF EXISTS] view_name;
+{% endhighlight %}
+
+### FUNCTION
+
+#### Show
+
+{% highlight sql %}
+SHOW FUNCTIONS;
+{% endhighlight %}
+
+#### Create
+
+{% highlight sql %}
+CREATE FUNCTION function_name AS class_name;
+{% endhighlight %}
+
+#### Drop
+
+{% highlight sql %}
+DROP FUNCTION [IF EXISTS] function_name;
+{% endhighlight %}
+
+## DML
+
+### INSERT
+
+{% highlight sql %}
+INSERT (INTO|OVERWRITE) [TABLE] table_name [PARTITION partition_spec] SELECT ...;
+{% endhighlight %}
+
+The `partition_spec`, if present, can be either a full spec or partial spec. If the `partition_spec` is a partial
+spec, the dynamic partition column names can be omitted.
+
+## DQL
+
+At the moment, Hive dialect supports the same syntax as Flink SQL for DQLs. Refer to
+[Flink SQL queries]({{ site.baseurl }}/zh/dev/table/sql/queries.html) for more details. And it's recommended to switch to
+`default` dialect to execute DQLs.
+
+## Notice
+
+The following are some precautions for using the Hive dialect.
+
+- Hive dialect should only be used to manipulate Hive tables, not generic tables. And Hive dialect should be used together
+with a [HiveCatalog]({{ site.baseurl }}/zh/dev/table/hive/hive_catalog.html).
+- While all Hive versions support the same syntax, whether a specific feature is available still depends on the
+[Hive version]({{ site.baseurl }}/zh/dev/table/hive/#支持的hive版本) you use. For example, updating database
+location is only supported in Hive-2.4.0 or later.
+- Hive and Calcite have different sets of reserved keywords. For example, `default` is a reserved keyword in Calcite and
+a non-reserved keyword in Hive. Even with Hive dialect, you have to quote such keywords with backtick ( ` ) in order to
+use them as identifiers.
+- Due to expanded query incompatibility, views created in Flink cannot be queried in Hive.
diff --git a/docs/dev/table/hive/hive_functions.zh.md b/docs/dev/table/hive/hive_functions.zh.md
index 537d684aa37d71..c2e46cf4f6dcb8 100644
--- a/docs/dev/table/hive/hive_functions.zh.md
+++ b/docs/dev/table/hive/hive_functions.zh.md
@@ -1,5 +1,5 @@
---
-title: "Hive functions"
+title: "Hive 函数"
nav-parent_id: hive_tableapi
nav-pos: 3
---
@@ -22,11 +22,11 @@ specific language governing permissions and limitations
under the License.
-->
-## Use Hive Built-in Functions via HiveModule
+## 通过 HiveModule 使用 Hive 内置函数
-The `HiveModule` provides Hive built-in functions as Flink system (built-in) functions to Flink SQL and Table API users.
+在 Flink SQL 和 Table API 中,可以通过系统内置的 `HiveModule` 来使用 Hive 内置函数,
-For detailed information, please refer to [HiveModule]({{ site.baseurl }}/dev/table/modules.html#hivemodule).
+详细信息,请参考 [HiveModule]({{ site.baseurl }}/zh/dev/table/modules.html#hivemodule)。
@@ -58,14 +58,14 @@ modules:
-* NOTE that some Hive built-in functions in older versions have [thread safety issues](https://issues.apache.org/jira/browse/HIVE-16183).
-We recommend users patch their own Hive to fix them.
+* 请注意旧版本的部分 Hive 内置函数存在[线程安全问题](https://issues.apache.org/jira/browse/HIVE-16183)。
+我们建议用户及时通过补丁修正 Hive 中的这些问题。
-## Hive User Defined Functions
+## Hive 用户自定义函数(User Defined Functions)
-Users can use their existing Hive User Defined Functions in Flink.
+在 Flink 中用户可以使用 Hive 里已经存在的 UDF 函数。
-Supported UDF types include:
+支持的 UDF 类型包括:
- UDF
- GenericUDF
@@ -73,24 +73,23 @@ Supported UDF types include:
- UDAF
- GenericUDAFResolver2
-Upon query planning and execution, Hive's UDF and GenericUDF are automatically translated into Flink's ScalarFunction,
-Hive's GenericUDTF is automatically translated into Flink's TableFunction,
-and Hive's UDAF and GenericUDAFResolver2 are translated into Flink's AggregateFunction.
+在进行查询规划和执行时,Hive UDF 和 GenericUDF 函数会自动转换成 Flink 中的 ScalarFunction,GenericUDTF 会被自动转换成 Flink 中的
+ TableFunction,UDAF 和 GenericUDAFResolver2 则转换成 Flink 聚合函数(AggregateFunction).
-To use a Hive User Defined Function, user have to
+想要使用 Hive UDF 函数,需要如下几步:
-- set a HiveCatalog backed by Hive Metastore that contains that function as current catalog of the session
-- include a jar that contains that function in Flink's classpath
-- use Blink planner.
+- 通过 Hive Metastore 将带有 UDF 的 HiveCatalog 设置为当前会话的 catalog 后端。
+- 将带有 UDF 的 jar 包放入 Flink classpath 中,并在代码中引入。
+- 使用 Blink planner。
-## Using Hive User Defined Functions
+## 使用 Hive UDF
-Assuming we have the following Hive functions registered in Hive Metastore:
+假设我们在 Hive Metastore 中已经注册了下面的 UDF 函数:
{% highlight java %}
/**
- * Test simple udf. Registered under name 'myudf'
+ * 注册为 'myudf' 的简单 UDF 测试类.
*/
public class TestHiveSimpleUDF extends UDF {
@@ -104,7 +103,7 @@ public class TestHiveSimpleUDF extends UDF {
}
/**
- * Test generic udf. Registered under name 'mygenericudf'
+ * 注册为 'mygenericudf' 的普通 UDF 测试类
*/
public class TestHiveGenericUDF extends GenericUDF {
@@ -137,7 +136,7 @@ public class TestHiveGenericUDF extends GenericUDF {
}
/**
- * Test split udtf. Registered under name 'mygenericudtf'
+ * 注册为 'mygenericudtf' 的字符串分割 UDF 测试类
*/
public class TestHiveUDTF extends GenericUDTF {
@@ -172,7 +171,7 @@ public class TestHiveUDTF extends GenericUDTF {
{% endhighlight %}
-From Hive CLI, we can see they are registered:
+在 Hive CLI 中,可以查询到已经注册的 UDF 函数:
{% highlight bash %}
hive> show functions;
@@ -184,8 +183,7 @@ myudtf
{% endhighlight %}
-
-Then, users can use them in SQL as:
+此时,用户如果想使用这些 UDF,在 SQL 中就可以这样写:
{% highlight bash %}
diff --git a/docs/dev/table/hive/hive_read_write.md b/docs/dev/table/hive/hive_read_write.md
new file mode 100644
index 00000000000000..5185cc7df7be96
--- /dev/null
+++ b/docs/dev/table/hive/hive_read_write.md
@@ -0,0 +1,228 @@
+---
+title: "Hive Read & Write"
+nav-parent_id: hive_tableapi
+nav-pos: 2
+---
+
+
+Using the `HiveCatalog` and Flink's connector to Hive, Flink can read and write from Hive data as an alternative to Hive's batch engine.
+Be sure to follow the instructions to include the correct [dependencies]({{ site.baseurl }}/dev/table/hive/#depedencies) in your application.
+And please also note that Hive connector only works with blink planner.
+
+* This will be replaced by the TOC
+{:toc}
+
+## Reading From Hive
+
+Assume Hive contains a single table in its `default` database, named people that contains several rows.
+
+{% highlight bash %}
+hive> show databases;
+OK
+default
+Time taken: 0.841 seconds, Fetched: 1 row(s)
+
+hive> show tables;
+OK
+Time taken: 0.087 seconds
+
+hive> CREATE TABLE mytable(name string, value double);
+OK
+Time taken: 0.127 seconds
+
+hive> SELECT * FROM mytable;
+OK
+Tom 4.72
+John 8.0
+Tom 24.2
+Bob 3.14
+Bob 4.72
+Tom 34.9
+Mary 4.79
+Tiff 2.72
+Bill 4.33
+Mary 77.7
+Time taken: 0.097 seconds, Fetched: 10 row(s)
+{% endhighlight %}
+
+With the data ready your can connect to Hive [connect to an existing Hive installation]({{ site.baseurl }}/dev/table/hive/#connecting-to-hive) and begin querying.
+
+{% highlight bash %}
+
+Flink SQL> show catalogs;
+myhive
+default_catalog
+
+# ------ Set the current catalog to be 'myhive' catalog if you haven't set it in the yaml file ------
+
+Flink SQL> use catalog myhive;
+
+# ------ See all registered database in catalog 'mytable' ------
+
+Flink SQL> show databases;
+default
+
+# ------ See the previously registered table 'mytable' ------
+
+Flink SQL> show tables;
+mytable
+
+# ------ The table schema that Flink sees is the same that we created in Hive, two columns - name as string and value as double ------
+Flink SQL> describe mytable;
+root
+ |-- name: name
+ |-- type: STRING
+ |-- name: value
+ |-- type: DOUBLE
+
+# ------ Select from hive table or hive view ------
+Flink SQL> SELECT * FROM mytable;
+
+ name value
+__________ __________
+
+ Tom 4.72
+ John 8.0
+ Tom 24.2
+ Bob 3.14
+ Bob 4.72
+ Tom 34.9
+ Mary 4.79
+ Tiff 2.72
+ Bill 4.33
+ Mary 77.7
+
+{% endhighlight %}
+
+### Querying Hive views
+
+If you need to query Hive views, please note:
+
+1. You have to use the Hive catalog as your current catalog before you can query views in that catalog. It can be done by either `tableEnv.useCatalog(...)` in Table API or `USE CATALOG ...` in SQL Client.
+2. Hive and Flink SQL have different syntax, e.g. different reserved keywords and literals. Make sure the view's query is compatible with Flink grammar.
+
+## Writing To Hive
+
+Similarly, data can be written into hive using an `INSERT` clause.
+
+Consider there is an example table named "mytable" with two columns: name and age, in string and int type.
+
+{% highlight bash %}
+# ------ INSERT INTO will append to the table or partition, keeping the existing data intact ------
+Flink SQL> INSERT INTO mytable SELECT 'Tom', 25;
+
+# ------ INSERT OVERWRITE will overwrite any existing data in the table or partition ------
+Flink SQL> INSERT OVERWRITE mytable SELECT 'Tom', 25;
+{% endhighlight %}
+
+We support partitioned table too, Consider there is a partitioned table named myparttable with four columns: name, age, my_type and my_date, in types ...... my_type and my_date are the partition keys.
+
+{% highlight bash %}
+# ------ Insert with static partition ------
+Flink SQL> INSERT OVERWRITE myparttable PARTITION (my_type='type_1', my_date='2019-08-08') SELECT 'Tom', 25;
+
+# ------ Insert with dynamic partition ------
+Flink SQL> INSERT OVERWRITE myparttable SELECT 'Tom', 25, 'type_1', '2019-08-08';
+
+# ------ Insert with static(my_type) and dynamic(my_date) partition ------
+Flink SQL> INSERT OVERWRITE myparttable PARTITION (my_type='type_1') SELECT 'Tom', 25, '2019-08-08';
+{% endhighlight %}
+
+
+## Formats
+
+We have tested on the following of table storage formats: text, csv, SequenceFile, ORC, and Parquet.
+
+
+## Optimizations
+
+### Partition Pruning
+
+Flink uses partition pruning as a performance optimization to limits the number of files and partitions
+that Flink reads when querying Hive tables. When your data is partitioned, Flink only reads a subset of the partitions in
+a Hive table when a query matches certain filter criteria.
+
+### Projection Pushdown
+
+Flink leverages projection pushdown to minimize data transfer between Flink and Hive tables by omitting
+unnecessary fields from table scans.
+
+It is especially beneficial when a table contains many columns.
+
+### Limit Pushdown
+
+For queries with LIMIT clause, Flink will limit the number of output records wherever possible to minimize the
+amount of data transferred across network.
+
+### Vectorized Optimization upon Read
+
+Optimization is used automatically when the following conditions are met:
+
+- Format: ORC or Parquet.
+- Columns without complex data type, like hive types: List, Map, Struct, Union.
+
+This feature is turned on by default. If there is a problem, you can use this config option to close Vectorized Optimization:
+
+{% highlight bash %}
+table.exec.hive.fallback-mapred-reader=true
+{% endhighlight %}
+
+### Source Parallelism Inference
+
+By default, Flink infers the hive source parallelism based on the number of splits, and the number of
+splits is based on the number of files and the number of blocks in the files.
+
+Flink allows you to flexibly configure the policy of parallelism inference. You can configure the
+following parameters in `TableConfig` (note that these parameters affect all sources of the job):
+
+
+
+
+
Key
+
Default
+
Type
+
Description
+
+
+
+
+
table.exec.hive.infer-source-parallelism
+
true
+
Boolean
+
If is true, source parallelism is inferred according to splits number. If is false, parallelism of source are set by config.
+
+
+
table.exec.hive.infer-source-parallelism.max
+
1000
+
Integer
+
Sets max infer parallelism for source operator.
+
+
+
+
+## Roadmap
+
+We are planning and actively working on supporting features like
+
+- ACID tables
+- bucketed tables
+- more formats
+
+Please reach out to the community for more feature request https://flink.apache.org/community.html#mailing-lists
diff --git a/docs/dev/table/hive/hive_read_write.zh.md b/docs/dev/table/hive/hive_read_write.zh.md
new file mode 100644
index 00000000000000..5185cc7df7be96
--- /dev/null
+++ b/docs/dev/table/hive/hive_read_write.zh.md
@@ -0,0 +1,228 @@
+---
+title: "Hive Read & Write"
+nav-parent_id: hive_tableapi
+nav-pos: 2
+---
+
+
+Using the `HiveCatalog` and Flink's connector to Hive, Flink can read and write from Hive data as an alternative to Hive's batch engine.
+Be sure to follow the instructions to include the correct [dependencies]({{ site.baseurl }}/dev/table/hive/#depedencies) in your application.
+And please also note that Hive connector only works with blink planner.
+
+* This will be replaced by the TOC
+{:toc}
+
+## Reading From Hive
+
+Assume Hive contains a single table in its `default` database, named people that contains several rows.
+
+{% highlight bash %}
+hive> show databases;
+OK
+default
+Time taken: 0.841 seconds, Fetched: 1 row(s)
+
+hive> show tables;
+OK
+Time taken: 0.087 seconds
+
+hive> CREATE TABLE mytable(name string, value double);
+OK
+Time taken: 0.127 seconds
+
+hive> SELECT * FROM mytable;
+OK
+Tom 4.72
+John 8.0
+Tom 24.2
+Bob 3.14
+Bob 4.72
+Tom 34.9
+Mary 4.79
+Tiff 2.72
+Bill 4.33
+Mary 77.7
+Time taken: 0.097 seconds, Fetched: 10 row(s)
+{% endhighlight %}
+
+With the data ready your can connect to Hive [connect to an existing Hive installation]({{ site.baseurl }}/dev/table/hive/#connecting-to-hive) and begin querying.
+
+{% highlight bash %}
+
+Flink SQL> show catalogs;
+myhive
+default_catalog
+
+# ------ Set the current catalog to be 'myhive' catalog if you haven't set it in the yaml file ------
+
+Flink SQL> use catalog myhive;
+
+# ------ See all registered database in catalog 'mytable' ------
+
+Flink SQL> show databases;
+default
+
+# ------ See the previously registered table 'mytable' ------
+
+Flink SQL> show tables;
+mytable
+
+# ------ The table schema that Flink sees is the same that we created in Hive, two columns - name as string and value as double ------
+Flink SQL> describe mytable;
+root
+ |-- name: name
+ |-- type: STRING
+ |-- name: value
+ |-- type: DOUBLE
+
+# ------ Select from hive table or hive view ------
+Flink SQL> SELECT * FROM mytable;
+
+ name value
+__________ __________
+
+ Tom 4.72
+ John 8.0
+ Tom 24.2
+ Bob 3.14
+ Bob 4.72
+ Tom 34.9
+ Mary 4.79
+ Tiff 2.72
+ Bill 4.33
+ Mary 77.7
+
+{% endhighlight %}
+
+### Querying Hive views
+
+If you need to query Hive views, please note:
+
+1. You have to use the Hive catalog as your current catalog before you can query views in that catalog. It can be done by either `tableEnv.useCatalog(...)` in Table API or `USE CATALOG ...` in SQL Client.
+2. Hive and Flink SQL have different syntax, e.g. different reserved keywords and literals. Make sure the view's query is compatible with Flink grammar.
+
+## Writing To Hive
+
+Similarly, data can be written into hive using an `INSERT` clause.
+
+Consider there is an example table named "mytable" with two columns: name and age, in string and int type.
+
+{% highlight bash %}
+# ------ INSERT INTO will append to the table or partition, keeping the existing data intact ------
+Flink SQL> INSERT INTO mytable SELECT 'Tom', 25;
+
+# ------ INSERT OVERWRITE will overwrite any existing data in the table or partition ------
+Flink SQL> INSERT OVERWRITE mytable SELECT 'Tom', 25;
+{% endhighlight %}
+
+We support partitioned table too, Consider there is a partitioned table named myparttable with four columns: name, age, my_type and my_date, in types ...... my_type and my_date are the partition keys.
+
+{% highlight bash %}
+# ------ Insert with static partition ------
+Flink SQL> INSERT OVERWRITE myparttable PARTITION (my_type='type_1', my_date='2019-08-08') SELECT 'Tom', 25;
+
+# ------ Insert with dynamic partition ------
+Flink SQL> INSERT OVERWRITE myparttable SELECT 'Tom', 25, 'type_1', '2019-08-08';
+
+# ------ Insert with static(my_type) and dynamic(my_date) partition ------
+Flink SQL> INSERT OVERWRITE myparttable PARTITION (my_type='type_1') SELECT 'Tom', 25, '2019-08-08';
+{% endhighlight %}
+
+
+## Formats
+
+We have tested on the following of table storage formats: text, csv, SequenceFile, ORC, and Parquet.
+
+
+## Optimizations
+
+### Partition Pruning
+
+Flink uses partition pruning as a performance optimization to limits the number of files and partitions
+that Flink reads when querying Hive tables. When your data is partitioned, Flink only reads a subset of the partitions in
+a Hive table when a query matches certain filter criteria.
+
+### Projection Pushdown
+
+Flink leverages projection pushdown to minimize data transfer between Flink and Hive tables by omitting
+unnecessary fields from table scans.
+
+It is especially beneficial when a table contains many columns.
+
+### Limit Pushdown
+
+For queries with LIMIT clause, Flink will limit the number of output records wherever possible to minimize the
+amount of data transferred across network.
+
+### Vectorized Optimization upon Read
+
+Optimization is used automatically when the following conditions are met:
+
+- Format: ORC or Parquet.
+- Columns without complex data type, like hive types: List, Map, Struct, Union.
+
+This feature is turned on by default. If there is a problem, you can use this config option to close Vectorized Optimization:
+
+{% highlight bash %}
+table.exec.hive.fallback-mapred-reader=true
+{% endhighlight %}
+
+### Source Parallelism Inference
+
+By default, Flink infers the hive source parallelism based on the number of splits, and the number of
+splits is based on the number of files and the number of blocks in the files.
+
+Flink allows you to flexibly configure the policy of parallelism inference. You can configure the
+following parameters in `TableConfig` (note that these parameters affect all sources of the job):
+
+
+
+
+
Key
+
Default
+
Type
+
Description
+
+
+
+
+
table.exec.hive.infer-source-parallelism
+
true
+
Boolean
+
If is true, source parallelism is inferred according to splits number. If is false, parallelism of source are set by config.
+
+
+
table.exec.hive.infer-source-parallelism.max
+
1000
+
Integer
+
Sets max infer parallelism for source operator.
+
+
+
+
+## Roadmap
+
+We are planning and actively working on supporting features like
+
+- ACID tables
+- bucketed tables
+- more formats
+
+Please reach out to the community for more feature request https://flink.apache.org/community.html#mailing-lists
diff --git a/docs/dev/table/hive/hive_streaming.md b/docs/dev/table/hive/hive_streaming.md
new file mode 100644
index 00000000000000..f5d94509052ade
--- /dev/null
+++ b/docs/dev/table/hive/hive_streaming.md
@@ -0,0 +1,200 @@
+---
+title: "Hive Streaming"
+nav-parent_id: hive_tableapi
+nav-pos: 2
+---
+
+
+A typical hive job is scheduled periodically to execute, so there will be a large delay.
+
+Flink supports to write, read and join the hive table in the form of streaming.
+
+* This will be replaced by the TOC
+{:toc}
+
+There are three types of streaming:
+
+- Writing streaming data into Hive table.
+- Reading Hive table incrementally in the form of streaming.
+- Streaming table join Hive table using [Temporal Table]({{ site.baseurl }}/dev/table/streaming/temporal_tables.html#temporal-table).
+
+## Streaming Writing
+
+The Hive table supports streaming writes, based on [Filesystem Streaming Sink]({{ site.baseurl }}/dev/table/connectors/filesystem.html#streaming-sink).
+
+The Hive Streaming Sink re-use Filesystem Streaming Sink to integrate Hadoop OutputFormat/RecordWriter to streaming writing.
+Hadoop RecordWriters are Bulk-encoded Formats, Bulk Formats rolls files on every checkpoint.
+
+By default, now only have renaming committer, this means S3 filesystem can not supports exactly-once,
+if you want to use Hive streaming sink in S3 filesystem, You can configure the following parameter to
+false to use Flink native writers (only work for parquet and orc) in `TableConfig` (note that these
+parameters affect all sinks of the job):
+
+
+
+
+
Key
+
Default
+
Type
+
Description
+
+
+
+
+
table.exec.hive.fallback-mapred-writer
+
true
+
Boolean
+
If it is false, using flink native writer to write parquet and orc files; if it is true, using hadoop mapred record writer to write parquet and orc files.
+
+
+
+
+The below shows how the streaming sink can be used to write a streaming query to write data from Kafka into a Hive table with partition-commit,
+and runs a batch query to read that data back out.
+
+{% highlight sql %}
+
+SET table.sql-dialect=hive;
+CREATE TABLE hive_table (
+ user_id STRING,
+ order_amount DOUBLE
+) PARTITIONED BY (dt STRING, hr STRING) STORED AS parquet TBLPROPERTIES (
+ 'partition.time-extractor.timestamp-pattern'='$dt $hr:00:00',
+ 'sink.partition-commit.trigger'='partition-time',
+ 'sink.partition-commit.delay'='1 h',
+ 'sink.partition-commit.policy.kind'='metastore,success-file'
+);
+
+SET table.sql-dialect=default;
+CREATE TABLE kafka_table (
+ user_id STRING,
+ order_amount DOUBLE,
+ log_ts TIMESTAMP(3),
+ WATERMARK FOR log_ts AS log_ts - INTERVAL '5' SECOND
+) WITH (...);
+
+-- streaming sql, insert into hive table
+INSERT INTO TABLE hive_table SELECT user_id, order_amount, DATE_FORMAT(log_ts, 'yyyy-MM-dd'), DATE_FORMAT(log_ts, 'HH') FROM kafka_table;
+
+-- batch sql, select with partition pruning
+SELECT * FROM hive_table WHERE dt='2020-05-20' and hr='12';
+
+{% endhighlight %}
+
+## Streaming Reading
+
+To improve the real-time performance of hive reading, Flink support real-time Hive table stream read:
+
+- Partition table, monitor the generation of partition, and read the new partition incrementally.
+- Non-partition table, monitor the generation of new files in the folder, and read new files incrementally.
+
+You can even use the 10 minute level partition strategy, and use Flink's Hive streaming reading and
+Hive streaming writing to greatly improve the real-time performance of Hive data warehouse to quasi
+real-time minute level.
+
+
+
+
+
Key
+
Default
+
Type
+
Description
+
+
+
+
+
streaming-source.enable
+
false
+
Boolean
+
Enable streaming source or not. NOTES: Please make sure that each partition/file should be written atomically, otherwise the reader may get incomplete data.
+
+
+
streaming-source.monitor-interval
+
1 m
+
Duration
+
Time interval for consecutively monitoring partition/file.
+
+
+
streaming-source.consume-order
+
create-time
+
String
+
The consume order of streaming source, support create-time and partition-time. create-time compare partition/file creation time, this is not the partition create time in Hive metaStore, but the folder/file modification time in filesystem; partition-time compare time represented by partition name, if the partition folder somehow gets updated, e.g. add new file into folder, it can affect how the data is consumed. For non-partition table, this value should always be 'create-time'.
+
+
+
streaming-source.consume-start-offset
+
1970-00-00
+
String
+
Start offset for streaming consuming. How to parse and compare offsets depends on your order. For create-time and partition-time, should be a timestamp string (yyyy-[m]m-[d]d [hh:mm:ss]). For partition-time, will use partition time extractor to extract time from partition.
+
+
+
+
+Note:
+
+- Monitor strategy is to scan all directories/files in location path now. If there are too many partitions, there will be performance problems.
+- Streaming reading for non-partitioned requires that each file should be put atomically into the target directory.
+- Streaming reading for partitioned requires that each partition should be add atomically in the view of hive metastore. This means that new data added to an existing partition won't be consumed.
+- Streaming reading not support watermark grammar in Flink DDL. So it can not be used for window operators.
+
+The below shows how to read Hive table incrementally.
+
+{% highlight sql %}
+
+SELECT * FROM hive_table /*+ OPTIONS('streaming-source.enable'='true', 'streaming-source.consume-start-offset'='2020-05-20') */;
+
+{% endhighlight %}
+
+## Hive Table As Temporal Tables
+
+You can use a Hive table as temporal table and join streaming data with it. Please follow
+the [example]({{ site.baseurl }}/dev/table/streaming/temporal_tables.html#temporal-table) to find out how to join a
+temporal table.
+
+When performing the join, the Hive table will be cached in TM memory and each record from the stream
+is looked up in the Hive table to decide whether a match is found. You don't need any extra settings to use a Hive table
+as temporal table. But optionally, you can configure the TTL of the Hive table cache with the following
+property. After the cache expires, the Hive table will be scanned again to load the latest data.
+
+
+
+
+
Key
+
Default
+
Type
+
Description
+
+
+
+
+
lookup.join.cache.ttl
+
60 min
+
Duration
+
The cache TTL (e.g. 10min) for the build table in lookup join. By default the TTL is 60 minutes.
+
+
+
+
+**Note**:
+1. Each joining subtask needs to keep its own cache of the Hive table. Please make sure the Hive table can fit into
+the memory of a TM task slot.
+2. You should set a relatively large value for `lookup.join.cache.ttl`. You'll probably have performance issue if
+your Hive table needs to be updated and reloaded too frequently.
+3. Currently we simply load the whole Hive table whenever the cache needs refreshing. There's no way to differentiate
+new data from the old.
diff --git a/docs/dev/table/hive/hive_streaming.zh.md b/docs/dev/table/hive/hive_streaming.zh.md
new file mode 100644
index 00000000000000..8046ee5b08b749
--- /dev/null
+++ b/docs/dev/table/hive/hive_streaming.zh.md
@@ -0,0 +1,200 @@
+---
+title: "Hive Streaming"
+nav-parent_id: hive_tableapi
+nav-pos: 2
+---
+
+
+A typical hive job is scheduled periodically to execute, so there will be a large delay.
+
+Flink supports to write, read and join the hive table in the form of streaming.
+
+* This will be replaced by the TOC
+{:toc}
+
+There are three types of streaming:
+
+- Writing streaming data into Hive table.
+- Reading Hive table incrementally in the form of streaming.
+- Streaming table join Hive table using [Temporal Table]({{ site.baseurl }}/dev/table/streaming/temporal_tables.html#temporal-table).
+
+## Streaming Writing
+
+The Hive table supports streaming writes, based on [Filesystem Streaming Sink]({{ site.baseurl }}/dev/table/connectors/filesystem.html#streaming-sink).
+
+The Hive Streaming Sink re-use Filesystem Streaming Sink to integrate Hadoop OutputFormat/RecordWriter to streaming writing.
+Hadoop RecordWriters are Bulk-encoded Formats, Bulk Formats rolls files on every checkpoint.
+
+By default, now only have renaming committer, this means S3 filesystem can not supports exactly-once,
+if you want to use Hive streaming sink in S3 filesystem, You can configure the following parameter to
+false to use Flink native writers (only work for parquet and orc) in `TableConfig` (note that these
+parameters affect all sinks of the job):
+
+
+
+
+
Key
+
Default
+
Type
+
Description
+
+
+
+
+
table.exec.hive.fallback-mapred-writer
+
true
+
Boolean
+
If it is false, using flink native writer to write parquet and orc files; if it is true, using hadoop mapred record writer to write parquet and orc files.
+
+
+
+
+The below shows how the streaming sink can be used to write a streaming query to write data from Kafka into a Hive table with partition-commit,
+and runs a batch query to read that data back out.
+
+{% highlight sql %}
+
+SET table.sql-dialect=hive;
+CREATE TABLE hive_table (
+ user_id STRING,
+ order_amount DOUBLE
+) PARTITIONED BY (dt STRING, hr STRING) STORED AS parquet TBLPROPERTIES (
+ 'partition.time-extractor.timestamp-pattern'='$dt $hr:00:00',
+ 'sink.partition-commit.trigger'='partition-time',
+ 'sink.partition-commit.delay'='1 h',
+ 'sink.partition-commit.policy.kind'='metastore,success-file'
+);
+
+SET table.sql-dialect=default;
+CREATE TABLE kafka_table (
+ user_id STRING,
+ order_amount DOUBLE,
+ log_ts TIMESTAMP(3),
+ WATERMARK FOR log_ts AS log_ts - INTERVAL '5' SECOND
+) WITH (...);
+
+-- streaming sql, insert into hive table
+INSERT INTO TABLE hive_table SELECT user_id, order_amount, DATE_FORMAT(log_ts, 'yyyy-MM-dd'), DATE_FORMAT(log_ts, 'HH') FROM kafka_table;
+
+-- batch sql, select with partition pruning
+SELECT * FROM hive_table WHERE dt='2020-05-20' and hr='12';
+
+{% endhighlight %}
+
+## Streaming Reading
+
+To improve the real-time performance of hive reading, Flink support real-time Hive table stream read:
+
+- Partition table, monitor the generation of partition, and read the new partition incrementally.
+- Non-partition table, monitor the generation of new files in the folder, and read new files incrementally.
+
+You can even use the 10 minute level partition strategy, and use Flink's Hive streaming reading and
+Hive streaming writing to greatly improve the real-time performance of Hive data warehouse to quasi
+real-time minute level.
+
+
+
+
+
Key
+
Default
+
Type
+
Description
+
+
+
+
+
streaming-source.enable
+
false
+
Boolean
+
Enable streaming source or not. NOTES: Please make sure that each partition/file should be written atomically, otherwise the reader may get incomplete data.
+
+
+
streaming-source.monitor-interval
+
1 m
+
Duration
+
Time interval for consecutively monitoring partition/file.
+
+
+
streaming-source.consume-order
+
create-time
+
String
+
The consume order of streaming source, support create-time and partition-time. create-time compare partition/file creation time, this is not the partition create time in Hive metaStore, but the folder/file modification time in filesystem; partition-time compare time represented by partition name, if the partition folder somehow gets updated, e.g. add new file into folder, it can affect how the data is consumed. For non-partition table, this value should always be 'create-time'.
+
+
+
streaming-source.consume-start-offset
+
1970-00-00
+
String
+
Start offset for streaming consuming. How to parse and compare offsets depends on your order. For create-time and partition-time, should be a timestamp string (yyyy-[m]m-[d]d [hh:mm:ss]). For partition-time, will use partition time extractor to extract time from partition.
+
+
+
+
+Note:
+
+- Monitor strategy is to scan all directories/files in location path now. If there are too many partitions, there will be performance problems.
+- Streaming reading for non-partitioned requires that each file should be put atomically into the target directory.
+- Streaming reading for partitioned requires that each partition should be add atomically in the view of hive metastore. This means that new data added to an existing partition won't be consumed.
+- Streaming reading not support watermark grammar in Flink DDL. So it can not be used for window operators.
+
+The below shows how to read Hive table incrementally.
+
+{% highlight sql %}
+
+SELECT * FROM hive_table /*+ OPTIONS('streaming-source.enable'='true', 'streaming-source.consume-start-offset'='2020-05-20') */;
+
+{% endhighlight %}
+
+## Hive Table As Temporal Tables
+
+You can use a Hive table as temporal table and join streaming data with it. Please follow
+the [example]({{ site.baseurl }}/zh/dev/table/streaming/temporal_tables.html#temporal-table) to find out how to join a
+temporal table.
+
+When performing the join, the Hive table will be cached in TM memory and each record from the stream
+is looked up in the Hive table to decide whether a match is found. You don't need any extra settings to use a Hive table
+as temporal table. But optionally, you can configure the TTL of the Hive table cache with the following
+property. After the cache expires, the Hive table will be scanned again to load the latest data.
+
+
+
+
+
Key
+
Default
+
Type
+
Description
+
+
+
+
+
lookup.join.cache.ttl
+
60 min
+
Duration
+
The cache TTL (e.g. 10min) for the build table in lookup join. By default the TTL is 60 minutes.
+
+
+
+
+**Note**:
+1. Each joining subtask needs to keep its own cache of the Hive table. Please make sure the Hive table can fit into
+the memory of a TM task slot.
+2. You should set a relatively large value for `lookup.join.cache.ttl`. You'll probably have performance issue if
+your Hive table needs to be updated and reloaded too frequently.
+3. Currently we simply load the whole Hive table whenever the cache needs refreshing. There's no way to differentiate
+new data from the old.
diff --git a/docs/dev/table/hive/index.md b/docs/dev/table/hive/index.md
index 3698df2571f845..35b53cd2604603 100644
--- a/docs/dev/table/hive/index.md
+++ b/docs/dev/table/hive/index.md
@@ -101,20 +101,12 @@ There are two ways to add Hive dependencies. First is to use Flink's bundled Hiv
The following tables list all available bundled hive jars. You can pick one to the `/lib/` directory in Flink distribution.
-{% if site.is_stable %}
-
| Metastore version | Maven dependency | SQL Client JAR |
| :---------------- | :--------------------------- | :----------------------|
-| 1.0.0 - 1.2.2 | `flink-connector-hive-1.2.2` | [Download](http://central.maven.org/maven2/org/apache/flink/flink-sql-connector-hive-1.2.2{{site.scala_version_suffix}}/{{site.version}}/flink-sql-connector-hive-1.2.2{{site.scala_version_suffix}}-{{site.version}}.jar) |
-| 2.0.0 - 2.2.0 | `flink-connector-hive-2.2.0` | [Download](http://central.maven.org/maven2/org/apache/flink/flink-sql-connector-hive-2.2.0{{site.scala_version_suffix}}/{{site.version}}/flink-sql-connector-hive-2.2.0{{site.scala_version_suffix}}-{{site.version}}.jar) |
-| 2.3.0 - 2.3.6 | `flink-connector-hive-2.3.6` | [Download](http://central.maven.org/maven2/org/apache/flink/flink-sql-connector-hive-2.3.6{{site.scala_version_suffix}}/{{site.version}}/flink-sql-connector-hive-2.3.6{{site.scala_version_suffix}}-{{site.version}}.jar) |
-| 3.0.0 - 3.1.2 | `flink-connector-hive-3.1.2` | [Download](http://central.maven.org/maven2/org/apache/flink/flink-sql-connector-hive-3.1.2{{site.scala_version_suffix}}/{{site.version}}/flink-sql-connector-hive-3.1.2{{site.scala_version_suffix}}-{{site.version}}.jar) |
-
-{% else %}
-
-These tables are only available for stable releases.
-
-{% endif %}
+| 1.0.0 - 1.2.2 | `flink-sql-connector-hive-1.2.2` | {% if site.is_stable %}[Download](https://repo.maven.apache.org/maven2/org/apache/flink/flink-sql-connector-hive-1.2.2{{site.scala_version_suffix}}/{{site.version}}/flink-sql-connector-hive-1.2.2{{site.scala_version_suffix}}-{{site.version}}.jar) {% else %} Only available for stable releases {% endif %} |
+| 2.0.0 - 2.2.0 | `flink-sql-connector-hive-2.2.0` | {% if site.is_stable %}[Download](https://repo.maven.apache.org/maven2/org/apache/flink/flink-sql-connector-hive-2.2.0{{site.scala_version_suffix}}/{{site.version}}/flink-sql-connector-hive-2.2.0{{site.scala_version_suffix}}-{{site.version}}.jar) {% else %} Only available for stable releases {% endif %} |
+| 2.3.0 - 2.3.6 | `flink-sql-connector-hive-2.3.6` | {% if site.is_stable %}[Download](https://repo.maven.apache.org/maven2/org/apache/flink/flink-sql-connector-hive-2.3.6{{site.scala_version_suffix}}/{{site.version}}/flink-sql-connector-hive-2.3.6{{site.scala_version_suffix}}-{{site.version}}.jar) {% else %} Only available for stable releases {% endif %} |
+| 3.0.0 - 3.1.2 | `flink-sql-connector-hive-3.1.2` | {% if site.is_stable %}[Download](https://repo.maven.apache.org/maven2/org/apache/flink/flink-sql-connector-hive-3.1.2{{site.scala_version_suffix}}/{{site.version}}/flink-sql-connector-hive-3.1.2{{site.scala_version_suffix}}-{{site.version}}.jar) {% else %} Only available for stable releases {% endif %} |
#### User defined dependencies
@@ -311,7 +303,7 @@ Take Hive version 2.3.4 for example:
{% highlight scala %}
-val settings = EnvironmentSettings.newInstance().useBlinkPlanner().inBatchMode().build()
+val settings = EnvironmentSettings.newInstance().inBatchMode().build()
val tableEnv = TableEnvironment.create(settings)
val name = "myhive"
@@ -364,4 +355,4 @@ catalogs:
## DML
-Flink 支持 DML 写入 Hive 表,请参考[读写 Hive 表]({{ site.baseurl }}/zh/dev/table/hive/read_write_hive.html)
+Flink 支持 DML 写入 Hive 表,请参考[读写 Hive 表]({{ site.baseurl }}/zh/dev/table/hive/hive_read_write.html)
diff --git a/docs/dev/table/hive/read_write_hive.md b/docs/dev/table/hive/read_write_hive.md
deleted file mode 100644
index 8aa83b91a07f55..00000000000000
--- a/docs/dev/table/hive/read_write_hive.md
+++ /dev/null
@@ -1,196 +0,0 @@
----
-title: "Reading & Writing Hive Tables"
-nav-parent_id: hive_tableapi
-nav-pos: 2
----
-
-
-Using the `HiveCatalog` and Flink's connector to Hive, Flink can read and write from Hive data as an alternative to Hive's batch engine.
-Be sure to follow the instructions to include the correct [dependencies]({{ site.baseurl }}/dev/table/hive/#depedencies) in your application.
-And please also note that Hive connector only works with blink planner.
-
-* This will be replaced by the TOC
-{:toc}
-
-## Reading From Hive
-
-Assume Hive contains a single table in its `default` database, named people that contains several rows.
-
-{% highlight bash %}
-hive> show databases;
-OK
-default
-Time taken: 0.841 seconds, Fetched: 1 row(s)
-
-hive> show tables;
-OK
-Time taken: 0.087 seconds
-
-hive> CREATE TABLE mytable(name string, value double);
-OK
-Time taken: 0.127 seconds
-
-hive> SELECT * FROM mytable;
-OK
-Tom 4.72
-John 8.0
-Tom 24.2
-Bob 3.14
-Bob 4.72
-Tom 34.9
-Mary 4.79
-Tiff 2.72
-Bill 4.33
-Mary 77.7
-Time taken: 0.097 seconds, Fetched: 10 row(s)
-{% endhighlight %}
-
-With the data ready your can connect to Hive [connect to an existing Hive installation]({{ site.baseurl }}/dev/table/hive/#connecting-to-hive) and begin querying.
-
-{% highlight bash %}
-
-Flink SQL> show catalogs;
-myhive
-default_catalog
-
-# ------ Set the current catalog to be 'myhive' catalog if you haven't set it in the yaml file ------
-
-Flink SQL> use catalog myhive;
-
-# ------ See all registered database in catalog 'mytable' ------
-
-Flink SQL> show databases;
-default
-
-# ------ See the previously registered table 'mytable' ------
-
-Flink SQL> show tables;
-mytable
-
-# ------ The table schema that Flink sees is the same that we created in Hive, two columns - name as string and value as double ------
-Flink SQL> describe mytable;
-root
- |-- name: name
- |-- type: STRING
- |-- name: value
- |-- type: DOUBLE
-
-# ------ Select from hive table or hive view ------
-Flink SQL> SELECT * FROM mytable;
-
- name value
-__________ __________
-
- Tom 4.72
- John 8.0
- Tom 24.2
- Bob 3.14
- Bob 4.72
- Tom 34.9
- Mary 4.79
- Tiff 2.72
- Bill 4.33
- Mary 77.7
-
-{% endhighlight %}
-
-### Querying Hive views
-
-If you need to query Hive views, please note:
-
-1. You have to use the Hive catalog as your current catalog before you can query views in that catalog. It can be done by either `tableEnv.useCatalog(...)` in Table API or `USE CATALOG ...` in SQL Client.
-2. Hive and Flink SQL have different syntax, e.g. different reserved keywords and literals. Make sure the view's query is compatible with Flink grammar.
-
-## Writing To Hive
-
-Similarly, data can be written into hive using an `INSERT` clause.
-
-Consider there is an example table named "mytable" with two columns: name and age, in string and int type.
-
-{% highlight bash %}
-# ------ INSERT INTO will append to the table or partition, keeping the existing data intact ------
-Flink SQL> INSERT INTO mytable SELECT 'Tom', 25;
-
-# ------ INSERT OVERWRITE will overwrite any existing data in the table or partition ------
-Flink SQL> INSERT OVERWRITE mytable SELECT 'Tom', 25;
-{% endhighlight %}
-
-We support partitioned table too, Consider there is a partitioned table named myparttable with four columns: name, age, my_type and my_date, in types ...... my_type and my_date are the partition keys.
-
-{% highlight bash %}
-# ------ Insert with static partition ------
-Flink SQL> INSERT OVERWRITE myparttable PARTITION (my_type='type_1', my_date='2019-08-08') SELECT 'Tom', 25;
-
-# ------ Insert with dynamic partition ------
-Flink SQL> INSERT OVERWRITE myparttable SELECT 'Tom', 25, 'type_1', '2019-08-08';
-
-# ------ Insert with static(my_type) and dynamic(my_date) partition ------
-Flink SQL> INSERT OVERWRITE myparttable PARTITION (my_type='type_1') SELECT 'Tom', 25, '2019-08-08';
-{% endhighlight %}
-
-
-## Formats
-
-We have tested on the following of table storage formats: text, csv, SequenceFile, ORC, and Parquet.
-
-
-## Optimizations
-
-### Partition Pruning
-
-Flink uses partition pruning as a performance optimization to limits the number of files and partitions
-that Flink reads when querying Hive tables. When your data is partitioned, Flink only reads a subset of the partitions in
-a Hive table when a query matches certain filter criteria.
-
-### Projection Pushdown
-
-Flink leverages projection pushdown to minimize data transfer between Flink and Hive tables by omitting
-unnecessary fields from table scans.
-
-It is especially beneficial when a table contains many columns.
-
-### Limit Pushdown
-
-For queries with LIMIT clause, Flink will limit the number of output records wherever possible to minimize the
-amount of data transferred across network.
-
-### Vectorized Optimization upon Read
-
-Optimization is used automatically when the following conditions are met:
-
-- Format: ORC or Parquet.
-- Columns without complex data type, like hive types: List, Map, Struct, Union.
-
-This feature is turned on by default. If there is a problem, you can use this config option to close Vectorized Optimization:
-
-{% highlight bash %}
-table.exec.hive.fallback-mapred-reader=true
-{% endhighlight %}
-
-
-## Roadmap
-
-We are planning and actively working on supporting features like
-
-- ACID tables
-- bucketed tables
-- more formats
-
-Please reach out to the community for more feature request https://flink.apache.org/community.html#mailing-lists
diff --git a/docs/dev/table/hive/read_write_hive.zh.md b/docs/dev/table/hive/read_write_hive.zh.md
deleted file mode 100644
index 8aa83b91a07f55..00000000000000
--- a/docs/dev/table/hive/read_write_hive.zh.md
+++ /dev/null
@@ -1,196 +0,0 @@
----
-title: "Reading & Writing Hive Tables"
-nav-parent_id: hive_tableapi
-nav-pos: 2
----
-
-
-Using the `HiveCatalog` and Flink's connector to Hive, Flink can read and write from Hive data as an alternative to Hive's batch engine.
-Be sure to follow the instructions to include the correct [dependencies]({{ site.baseurl }}/dev/table/hive/#depedencies) in your application.
-And please also note that Hive connector only works with blink planner.
-
-* This will be replaced by the TOC
-{:toc}
-
-## Reading From Hive
-
-Assume Hive contains a single table in its `default` database, named people that contains several rows.
-
-{% highlight bash %}
-hive> show databases;
-OK
-default
-Time taken: 0.841 seconds, Fetched: 1 row(s)
-
-hive> show tables;
-OK
-Time taken: 0.087 seconds
-
-hive> CREATE TABLE mytable(name string, value double);
-OK
-Time taken: 0.127 seconds
-
-hive> SELECT * FROM mytable;
-OK
-Tom 4.72
-John 8.0
-Tom 24.2
-Bob 3.14
-Bob 4.72
-Tom 34.9
-Mary 4.79
-Tiff 2.72
-Bill 4.33
-Mary 77.7
-Time taken: 0.097 seconds, Fetched: 10 row(s)
-{% endhighlight %}
-
-With the data ready your can connect to Hive [connect to an existing Hive installation]({{ site.baseurl }}/dev/table/hive/#connecting-to-hive) and begin querying.
-
-{% highlight bash %}
-
-Flink SQL> show catalogs;
-myhive
-default_catalog
-
-# ------ Set the current catalog to be 'myhive' catalog if you haven't set it in the yaml file ------
-
-Flink SQL> use catalog myhive;
-
-# ------ See all registered database in catalog 'mytable' ------
-
-Flink SQL> show databases;
-default
-
-# ------ See the previously registered table 'mytable' ------
-
-Flink SQL> show tables;
-mytable
-
-# ------ The table schema that Flink sees is the same that we created in Hive, two columns - name as string and value as double ------
-Flink SQL> describe mytable;
-root
- |-- name: name
- |-- type: STRING
- |-- name: value
- |-- type: DOUBLE
-
-# ------ Select from hive table or hive view ------
-Flink SQL> SELECT * FROM mytable;
-
- name value
-__________ __________
-
- Tom 4.72
- John 8.0
- Tom 24.2
- Bob 3.14
- Bob 4.72
- Tom 34.9
- Mary 4.79
- Tiff 2.72
- Bill 4.33
- Mary 77.7
-
-{% endhighlight %}
-
-### Querying Hive views
-
-If you need to query Hive views, please note:
-
-1. You have to use the Hive catalog as your current catalog before you can query views in that catalog. It can be done by either `tableEnv.useCatalog(...)` in Table API or `USE CATALOG ...` in SQL Client.
-2. Hive and Flink SQL have different syntax, e.g. different reserved keywords and literals. Make sure the view's query is compatible with Flink grammar.
-
-## Writing To Hive
-
-Similarly, data can be written into hive using an `INSERT` clause.
-
-Consider there is an example table named "mytable" with two columns: name and age, in string and int type.
-
-{% highlight bash %}
-# ------ INSERT INTO will append to the table or partition, keeping the existing data intact ------
-Flink SQL> INSERT INTO mytable SELECT 'Tom', 25;
-
-# ------ INSERT OVERWRITE will overwrite any existing data in the table or partition ------
-Flink SQL> INSERT OVERWRITE mytable SELECT 'Tom', 25;
-{% endhighlight %}
-
-We support partitioned table too, Consider there is a partitioned table named myparttable with four columns: name, age, my_type and my_date, in types ...... my_type and my_date are the partition keys.
-
-{% highlight bash %}
-# ------ Insert with static partition ------
-Flink SQL> INSERT OVERWRITE myparttable PARTITION (my_type='type_1', my_date='2019-08-08') SELECT 'Tom', 25;
-
-# ------ Insert with dynamic partition ------
-Flink SQL> INSERT OVERWRITE myparttable SELECT 'Tom', 25, 'type_1', '2019-08-08';
-
-# ------ Insert with static(my_type) and dynamic(my_date) partition ------
-Flink SQL> INSERT OVERWRITE myparttable PARTITION (my_type='type_1') SELECT 'Tom', 25, '2019-08-08';
-{% endhighlight %}
-
-
-## Formats
-
-We have tested on the following of table storage formats: text, csv, SequenceFile, ORC, and Parquet.
-
-
-## Optimizations
-
-### Partition Pruning
-
-Flink uses partition pruning as a performance optimization to limits the number of files and partitions
-that Flink reads when querying Hive tables. When your data is partitioned, Flink only reads a subset of the partitions in
-a Hive table when a query matches certain filter criteria.
-
-### Projection Pushdown
-
-Flink leverages projection pushdown to minimize data transfer between Flink and Hive tables by omitting
-unnecessary fields from table scans.
-
-It is especially beneficial when a table contains many columns.
-
-### Limit Pushdown
-
-For queries with LIMIT clause, Flink will limit the number of output records wherever possible to minimize the
-amount of data transferred across network.
-
-### Vectorized Optimization upon Read
-
-Optimization is used automatically when the following conditions are met:
-
-- Format: ORC or Parquet.
-- Columns without complex data type, like hive types: List, Map, Struct, Union.
-
-This feature is turned on by default. If there is a problem, you can use this config option to close Vectorized Optimization:
-
-{% highlight bash %}
-table.exec.hive.fallback-mapred-reader=true
-{% endhighlight %}
-
-
-## Roadmap
-
-We are planning and actively working on supporting features like
-
-- ACID tables
-- bucketed tables
-- more formats
-
-Please reach out to the community for more feature request https://flink.apache.org/community.html#mailing-lists
diff --git a/docs/dev/table/hive/scala_shell_hive.md b/docs/dev/table/hive/scala_shell_hive.md
deleted file mode 100644
index d3fbd9195abaae..00000000000000
--- a/docs/dev/table/hive/scala_shell_hive.md
+++ /dev/null
@@ -1,45 +0,0 @@
----
-title: "Use Hive connector in Scala Shell"
-nav-parent_id: hive_tableapi
-nav-pos: 3
----
-
-
-NOTE: since blink planner is not well supported in Scala Shell at the moment, it's **NOT** recommended to use Hive connector in Scala Shell.
-
-[Flink Scala Shell]({{ site.baseurl }}/ops/scala_shell.html) is a convenient quick way to try flink.
-You can use hive in Scala Shell as well instead of specifying hive dependencies in pom file, packaging your program and submitting it via flink run command.
-In order to use hive connector in Scala Shell, you need to put the following [hive connector dependencies]({{ site.baseurl }}/dev/table/hive/#depedencies) under lib folder of flink dist .
-
-* flink-connector-hive_{scala_version}-{flink.version}.jar
-* flink-hadoop-compatibility_{scala_version}-{flink.version}.jar
-* flink-shaded-hadoop-2-uber-{hadoop.version}-{flink-shaded.version}.jar
-* hive-exec-2.x.jar (for Hive 1.x, you need to copy hive-exec-1.x.jar, hive-metastore-1.x.jar, libfb303-0.9.2.jar and libthrift-0.9.2.jar)
-
-Then you can use hive connector in Scala Shell like following:
-
-{% highlight scala %}
-Scala-Flink> import org.apache.flink.table.catalog.hive.HiveCatalog
-Scala-Flink> val hiveCatalog = new HiveCatalog("hive", "default", "", "2.3.4");
-Scala-Flink> btenv.registerCatalog("hive", hiveCatalog)
-Scala-Flink> btenv.useCatalog("hive")
-Scala-Flink> btenv.listTables
-Scala-Flink> btenv.sqlQuery("").toDataSet[Row].print()
-{% endhighlight %}
diff --git a/docs/dev/table/hive/scala_shell_hive.zh.md b/docs/dev/table/hive/scala_shell_hive.zh.md
deleted file mode 100644
index b9cefc65760b98..00000000000000
--- a/docs/dev/table/hive/scala_shell_hive.zh.md
+++ /dev/null
@@ -1,45 +0,0 @@
----
-title: "在 Scala Shell 中使用 Hive 连接器"
-nav-parent_id: hive_tableapi
-nav-pos: 3
----
-
-
-注意:目前 blink planner 还不能很好的支持 Scala Shell,因此 **不** 建议在 Scala Shell 中使用 Hive 连接器。
-
-[Flink Scala Shell]({{ site.baseurl }}/zh/ops/scala_shell.html) 是快速上手 Flink 的好方法。
-你可以在 Scala Shell 中直接使用 Hive 连接器,而不需要在 pom 中引入 Hive 相关依赖,并打包提交作业。
-想要在 Scala Shell 中使用 Hive 连接器,你需要把 [Hive 连接器依赖项]({{ site.baseurl }}/zh/dev/table/hive/#depedencies) 放在 Flink dist 包中的 lib 文件夹下。
-
-* flink-connector-hive_{scala_version}-{flink.version}.jar
-* flink-hadoop-compatibility_{scala_version}-{flink.version}.jar
-* flink-shaded-hadoop-2-uber-{hadoop.version}-{flink-shaded.version}.jar
-* hive-exec-2.x.jar (对于 Hive 1.x 版本,你需要复制 hive-exec-1.x.jar, hive-metastore-1.x.jar, libfb303-0.9.2.jar and libthrift-0.9.2.jar)
-
-然后你就可以在 Scala Shell 中使用 Hive 连接器,如下所示:
-
-{% highlight scala %}
-Scala-Flink> import org.apache.flink.table.catalog.hive.HiveCatalog
-Scala-Flink> val hiveCatalog = new HiveCatalog("hive", "default", "", "2.3.4");
-Scala-Flink> btenv.registerCatalog("hive", hiveCatalog)
-Scala-Flink> btenv.useCatalog("hive")
-Scala-Flink> btenv.listTables
-Scala-Flink> btenv.sqlQuery("").toDataSet[Row].print()
-{% endhighlight %}
diff --git a/docs/dev/table/index.md b/docs/dev/table/index.md
index 165f7de0125f78..eb6b700529db59 100644
--- a/docs/dev/table/index.md
+++ b/docs/dev/table/index.md
@@ -38,7 +38,7 @@ Starting from Flink 1.9, Flink provides two different planner implementations fo
translating relational operators into an executable, optimized Flink job. Both of the planners come with different optimization rules and runtime classes.
They may also differ in the set of supported features.
-Attention For production use cases, we recommend the old planner that was present before Flink 1.9 for now.
+Attention For production use cases, we recommend the blink planner that has become the default planner since 1.11.
All Table API and SQL components are bundled in the `flink-table` or `flink-table-blink` Maven artifacts.
@@ -49,8 +49,8 @@ The following dependencies are relevant for most projects:
* `flink-table-api-scala`: The Table & SQL API for pure table programs using the Scala programming language (in early development stage, not recommended!).
* `flink-table-api-java-bridge`: The Table & SQL API with DataStream/DataSet API support using the Java programming language.
* `flink-table-api-scala-bridge`: The Table & SQL API with DataStream/DataSet API support using the Scala programming language.
-* `flink-table-planner`: The table program planner and runtime. This was the only planner of Flink before the 1.9 release. It is still the recommended one.
-* `flink-table-planner-blink`: The new Blink planner.
+* `flink-table-planner`: The table program planner and runtime. This was the only planner of Flink before the 1.9 release. It's no longer recommended since Flink 1.11.
+* `flink-table-planner-blink`: The new Blink planner, which has become the default one since Flink 1.11.
* `flink-table-runtime-blink`: The new Blink runtime.
* `flink-table-uber`: Packages the API modules above plus the old planner into a distribution for most Table & SQL API use cases. The uber JAR file `flink-table-*.jar` is located in the `/lib` directory of a Flink release by default.
* `flink-table-uber-blink`: Packages the API modules above plus the Blink specific modules into a distribution for most Table & SQL API use cases. The uber JAR file `flink-table-blink-*.jar` is located in the `/lib` directory of a Flink release by default.
diff --git a/docs/dev/table/index.zh.md b/docs/dev/table/index.zh.md
index 8613b117830dc0..e7bb0f5dd4e7e3 100644
--- a/docs/dev/table/index.zh.md
+++ b/docs/dev/table/index.zh.md
@@ -27,7 +27,7 @@ under the License.
Apache Flink 有两种关系型 API 来做流批统一处理:Table API 和 SQL。Table API 是用于 Scala 和 Java 语言的查询API,它可以用一种非常直观的方式来组合使用选取、过滤、join 等关系型算子。Flink SQL 是基于 [Apache Calcite](https://calcite.apache.org) 来实现的标准 SQL。这两种 API 中的查询对于批(DataSet)和流(DataStream)的输入有相同的语义,也会产生同样的计算结果。
-Table API 和 SQL 两种 API 是紧密集成的,以及 DataStream 和 DataSet API。你可以在这些 API 之间,以及一些基于这些 API 的库之间轻松的切换。比如,你可以先用 [CEP]({{ site.baseurl }}/zh/dev/libs/cep.html) 从 DataStream 中做模式匹配,然后用 Table API 来分析匹配的结果;或者你可以用 SQL 来扫描、过滤、聚合一个批式的表,然后再跑一个 [Gelly 图算法]({{ site.baseurl }}/zh/dev/libs/gelly) 来处理已经预处理好的数据。
+Table API 和 SQL 两种 API 是紧密集成的,以及 DataStream 和 DataSet API。你可以在这些 API 之间,以及一些基于这些 API 的库之间轻松的切换。比如,你可以先用 [CEP]({{ site.baseurl }}/zh/dev/libs/cep.html) 从 DataStream 中做模式匹配,然后用 Table API 来分析匹配的结果;或者你可以用 SQL 来扫描、过滤、聚合一个批式的表,然后再跑一个 [Gelly 图算法]({{ site.baseurl }}/zh/dev/libs/gelly/index.html) 来处理已经预处理好的数据。
**注意:Table API 和 SQL 现在还处于活跃开发阶段,还没有完全实现所有的特性。不是所有的 \[Table API,SQL\] 和 \[流,批\] 的组合都是支持的。**
@@ -38,7 +38,7 @@ Table API 和 SQL 两种 API 是紧密集成的,以及 DataStream 和 DataSet
Planner 的作用主要是把关系型的操作翻译成可执行的、经过优化的 Flink 任务。两种 Planner 所使用的优化规则以及运行时类都不一样。
它们在支持的功能上也有些差异。
-注意 对于生产环境,我们建议使用在1.9之前就已经存在的 Old Planner。
+注意 对于生产环境,我们建议使用在1.11版本之后已经变成默认的Blink Planner。
所有的 Table API 和 SQL 的代码都在 `flink-table` 或者 `flink-table-blink` Maven artifacts 下。
@@ -49,8 +49,8 @@ Planner 的作用主要是把关系型的操作翻译成可执行的、经过优
* `flink-table-api-scala`: Table 和 SQL API,使用 Scala 语言编写的,给纯 table 程序使用(还在早期开发阶段,不建议使用)
* `flink-table-api-java-bridge`: Table 和 SQL API 结合 DataStream/DataSet API 一起使用,给 Java 语言使用。
* `flink-table-api-scala-bridge`: Table 和 SQL API 结合 DataStream/DataSet API 一起使用,给 Scala 语言使用。
-* `flink-table-planner`: table Planner 和运行时。这是在1.9之前 Flink 的唯一的 Planner,现在仍然建议使用这个。
-* `flink-table-planner-blink`: 新的 Blink Planner。
+* `flink-table-planner`: table Planner 和运行时。这是在1.9之前 Flink 的唯一的 Planner,但是从1.11版本开始我们不推荐继续使用。
+* `flink-table-planner-blink`: 新的 Blink Planner,从1.11版本开始成为默认的 Planner。
* `flink-table-runtime-blink`: 新的 Blink 运行时。
* `flink-table-uber`: 把上述模块以及 Old Planner 打包到一起,可以在大部分 Table & SQL API 场景下使用。打包到一起的 jar 文件 `flink-table-*.jar` 默认会直接放到 Flink 发行版的 `/lib` 目录下。
* `flink-table-uber-blink`: 把上述模块以及 Blink Planner 打包到一起,可以在大部分 Table & SQL API 场景下使用。打包到一起的 jar 文件 `flink-table-blink-*.jar` 默认会放到 Flink 发行版的 `/lib` 目录下。
diff --git a/docs/dev/table/legacySourceSinks.md b/docs/dev/table/legacySourceSinks.md
new file mode 100644
index 00000000000000..3d0d0a8b6c5026
--- /dev/null
+++ b/docs/dev/table/legacySourceSinks.md
@@ -0,0 +1,815 @@
+---
+title: "Legacy Table Source & Sink Interfaces"
+---
+
+
+A `TableSource` provides access to data which is stored in external systems (database, key-value store, message queue) or files. After a [TableSource is registered in a TableEnvironment]({% link dev/table/common.md %}#register-a-tablesource) it can be accessed by [Table API]({% link dev/table/tableApi.md %}) or [SQL]({% link dev/table/sql/queries.md %}) queries.
+
+A `TableSink` [emits a Table]({% link dev/table/common.md %}#emit-a-table) to an external storage system, such as a database, key-value store, message queue, or file system (in different encodings, e.g., CSV, Parquet, or ORC).
+
+A `TableFactory` allows for separating the declaration of a connection to an external system from the actual implementation. A table factory creates configured instances of table sources and sinks from normalized, string-based properties. The properties can be generated programmatically using a `Descriptor` or via YAML configuration files for the [SQL Client]({% link dev/table/sqlClient.md %}).
+
+Have a look at the [common concepts and API]({% link dev/table/common.md %}) page for details how to [register a TableSource]({% link dev/table/common.md %}#register-a-tablesource) and how to [emit a Table through a TableSink]({% link dev/table/common.md %}#emit-a-table). See the [built-in sources, sinks, and formats]({% link dev/table/connect.md %}) page for examples how to use factories.
+
+* This will be replaced by the TOC
+{:toc}
+
+Define a TableSource
+--------------------
+
+A `TableSource` is a generic interface that gives Table API and SQL queries access to data stored in an external system. It provides the schema of the table and the records that are mapped to rows with the table's schema. Depending on whether the `TableSource` is used in a streaming or batch query, the records are produced as a `DataSet` or `DataStream`.
+
+If a `TableSource` is used in a streaming query it must implement the `StreamTableSource` interface, if it is used in a batch query it must implement the `BatchTableSource` interface. A `TableSource` can also implement both interfaces and be used in streaming and batch queries.
+
+`StreamTableSource` and `BatchTableSource` extend the base interface `TableSource` that defines the following methods:
+
+
+
+{% highlight java %}
+TableSource {
+
+ public TableSchema getTableSchema();
+
+ public TypeInformation getReturnType();
+
+ public String explainSource();
+}
+{% endhighlight %}
+
+
+* `getTableSchema()`: Returns the schema of the produced table, i.e., the names and types of the fields of the table. The field types are defined using Flink's `DataType` (see [Table API types]({% link dev/table/types.md %}) and [SQL types]({% link dev/table/sql/index.md %}#data-types)). Note that the returned `TableSchema` shouldn't contain computed columns to reflect the schema of the physical `TableSource`.
+
+* `getReturnType()`: Returns the physical type of the `DataStream` (`StreamTableSource`) or `DataSet` (`BatchTableSource`) and the records that are produced by the `TableSource`.
+
+* `explainSource()`: Returns a String that describes the `TableSource`. This method is optional and used for display purposes only.
+
+The `TableSource` interface separates the logical table schema from the physical type of the returned `DataStream` or `DataSet`. As a consequence, all fields of the table schema (`getTableSchema()`) must be mapped to a field with corresponding type of the physical return type (`getReturnType()`). By default, this mapping is done based on field names. For example, a `TableSource` that defines a table schema with two fields `[name: String, size: Integer]` requires a `TypeInformation` with at least two fields called `name` and `size` of type `String` and `Integer`, respectively. This could be a `PojoTypeInfo` or a `RowTypeInfo` that have two fields named `name` and `size` with matching types.
+
+However, some types, such as Tuple or CaseClass types, do support custom field names. If a `TableSource` returns a `DataStream` or `DataSet` of a type with fixed field names, it can implement the `DefinedFieldMapping` interface to map field names from the table schema to field names of the physical return type.
+
+### Defining a BatchTableSource
+
+The `BatchTableSource` interface extends the `TableSource` interface and defines one additional method:
+
+
+
+* `getDataSet(execEnv)`: Returns a `DataSet` with the data of the table. The type of the `DataSet` must be identical to the return type defined by the `TableSource.getReturnType()` method. The `DataSet` can by created using a regular [data source]({% link dev/batch/index.md %}#data-sources) of the DataSet API. Commonly, a `BatchTableSource` is implemented by wrapping a `InputFormat` or [batch connector]({% link dev/batch/connectors.md %}).
+
+{% top %}
+
+### Defining a StreamTableSource
+
+The `StreamTableSource` interface extends the `TableSource` interface and defines one additional method:
+
+
+
+* `getDataStream(execEnv)`: Returns a `DataStream` with the data of the table. The type of the `DataStream` must be identical to the return type defined by the `TableSource.getReturnType()` method. The `DataStream` can by created using a regular [data source]({% link dev/datastream_api.md %}#data-sources) of the DataStream API. Commonly, a `StreamTableSource` is implemented by wrapping a `SourceFunction` or a [stream connector]({% link dev/connectors/index.md %}).
+
+{% top %}
+
+### Defining a TableSource with Time Attributes
+
+Time-based operations of streaming [Table API]({% link dev/table/tableApi.md %}#group-windows) and [SQL]({% link dev/table/sql/queries.md %}#group-windows) queries, such as windowed aggregations or joins, require explicitly specified [time attributes]({% link dev/table/streaming/time_attributes.md %}).
+
+A `TableSource` defines a time attribute as a field of type `Types.SQL_TIMESTAMP` in its table schema. In contrast to all regular fields in the schema, a time attribute must not be matched to a physical field in the return type of the table source. Instead, a `TableSource` defines a time attribute by implementing a certain interface.
+
+#### Defining a Processing Time Attribute
+
+[Processing time attributes]({% link dev/table/streaming/time_attributes.md %}#processing-time) are commonly used in streaming queries. A processing time attribute returns the current wall-clock time of the operator that accesses it. A `TableSource` defines a processing time attribute by implementing the `DefinedProctimeAttribute` interface. The interface looks as follows:
+
+
+
+* `getProctimeAttribute()`: Returns the name of the processing time attribute. The specified attribute must be defined of type `Types.SQL_TIMESTAMP` in the table schema and can be used in time-based operations. A `DefinedProctimeAttribute` table source can define no processing time attribute by returning `null`.
+
+Attention Both `StreamTableSource` and `BatchTableSource` can implement `DefinedProctimeAttribute` and define a processing time attribute. In case of a `BatchTableSource` the processing time field is initialized with the current timestamp during the table scan.
+
+#### Defining a Rowtime Attribute
+
+[Rowtime attributes]({% link dev/table/streaming/time_attributes.md %}#event-time) are attributes of type `TIMESTAMP` and handled in a unified way in stream and batch queries.
+
+A table schema field of type `SQL_TIMESTAMP` can be declared as rowtime attribute by specifying
+
+* the name of the field,
+* a `TimestampExtractor` that computes the actual value for the attribute (usually from one or more other fields), and
+* a `WatermarkStrategy` that specifies how watermarks are generated for the the rowtime attribute.
+
+A `TableSource` defines a rowtime attribute by implementing the `DefinedRowtimeAttributes` interface. The interface looks as follows:
+
+
+
+{% highlight java %}
+DefinedRowtimeAttribute {
+
+ public List getRowtimeAttributeDescriptors();
+}
+{% endhighlight %}
+
+
+* `getRowtimeAttributeDescriptors()`: Returns a list of `RowtimeAttributeDescriptor`. A `RowtimeAttributeDescriptor` describes a rowtime attribute with the following properties:
+ * `attributeName`: The name of the rowtime attribute in the table schema. The field must be defined with type `Types.SQL_TIMESTAMP`.
+ * `timestampExtractor`: The timestamp extractor extracts the timestamp from a record with the return type. For example, it can convert a Long field into a timestamp or parse a String-encoded timestamp. Flink comes with a set of built-in `TimestampExtractor` implementation for common use cases. It is also possible to provide a custom implementation.
+ * `watermarkStrategy`: The watermark strategy defines how watermarks are generated for the rowtime attribute. Flink comes with a set of built-in `WatermarkStrategy` implementations for common use cases. It is also possible to provide a custom implementation.
+
+Attention Although the `getRowtimeAttributeDescriptors()` method returns a list of descriptors, only a single rowtime attribute is support at the moment. We plan to remove this restriction in the future and support tables with more than one rowtime attribute.
+
+Attention Both, `StreamTableSource` and `BatchTableSource`, can implement `DefinedRowtimeAttributes` and define a rowtime attribute. In either case, the rowtime field is extracted using the `TimestampExtractor`. Hence, a `TableSource` that implements `StreamTableSource` and `BatchTableSource` and defines a rowtime attribute provides exactly the same data to streaming and batch queries.
+
+##### Provided Timestamp Extractors
+
+Flink provides `TimestampExtractor` implementations for common use cases.
+
+The following `TimestampExtractor` implementations are currently available:
+
+* `ExistingField(fieldName)`: Extracts the value of a rowtime attribute from an existing `LONG`, `SQL_TIMESTAMP`, or timestamp formatted `STRING` field. One example of such a string would be '2018-05-28 12:34:56.000'.
+* `StreamRecordTimestamp()`: Extracts the value of a rowtime attribute from the timestamp of the `DataStream` `StreamRecord`. Note, this `TimestampExtractor` is not available for batch table sources.
+
+A custom `TimestampExtractor` can be defined by implementing the corresponding interface.
+
+##### Provided Watermark Strategies
+
+Flink provides `WatermarkStrategy` implementations for common use cases.
+
+The following `WatermarkStrategy` implementations are currently available:
+
+* `AscendingTimestamps`: A watermark strategy for ascending timestamps. Records with timestamps that are out-of-order will be considered late.
+* `BoundedOutOfOrderTimestamps(delay)`: A watermark strategy for timestamps that are at most out-of-order by the specified delay.
+* `PreserveWatermarks()`: A strategy which indicates the watermarks should be preserved from the underlying `DataStream`.
+
+A custom `WatermarkStrategy` can be defined by implementing the corresponding interface.
+
+{% top %}
+
+### Defining a TableSource with Projection Push-Down
+
+A `TableSource` supports projection push-down by implementing the `ProjectableTableSource` interface. The interface defines a single method:
+
+
+
+* `projectFields(fields)`: Returns a *copy* of the `TableSource` with adjusted physical return type. The `fields` parameter provides the indexes of the fields that must be provided by the `TableSource`. The indexes relate to the `TypeInformation` of the physical return type, *not* to the logical table schema. The copied `TableSource` must adjust its return type and the returned `DataStream` or `DataSet`. The `TableSchema` of the copied `TableSource` must not be changed, i.e, it must be the same as the original `TableSource`. If the `TableSource` implements the `DefinedFieldMapping` interface, the field mapping must be adjusted to the new return type.
+
+Attention In order for Flink to distinguish a projection push-down table source from its original form, `explainSource` method must be override to include information regarding the projected fields.
+
+The `ProjectableTableSource` adds support to project flat fields. If the `TableSource` defines a table with nested schema, it can implement the `NestedFieldsProjectableTableSource` to extend the projection to nested fields. The `NestedFieldsProjectableTableSource` is defined as follows:
+
+
+
+* `projectNestedField(fields, nestedFields)`: Returns a *copy* of the `TableSource` with adjusted physical return type. Fields of the physical return type may be removed or reordered but their type must not be changed. The contract of this method is essentially the same as for the `ProjectableTableSource.projectFields()` method. In addition, the `nestedFields` parameter contains for each field index in the `fields` list, a list of paths to all nested fields that are accessed by the query. All other nested fields do not need to be read, parsed, and set in the records that are produced by the `TableSource`.
+
+Attention the types of the projected fields must not be changed but unused fields may be set to null or to a default value.
+
+{% top %}
+
+### Defining a TableSource with Filter Push-Down
+
+The `FilterableTableSource` interface adds support for filter push-down to a `TableSource`. A `TableSource` extending this interface is able to filter records such that the returned `DataStream` or `DataSet` returns fewer records.
+
+The interface looks as follows:
+
+
+
+* `applyPredicate(predicates)`: Returns a *copy* of the `TableSource` with added predicates. The `predicates` parameter is a mutable list of conjunctive predicates that are "offered" to the `TableSource`. The `TableSource` accepts to evaluate a predicate by removing it from the list. Predicates that are left in the list will be evaluated by a subsequent filter operator.
+* `isFilterPushedDown()`: Returns true if the `applyPredicate()` method was called before. Hence, `isFilterPushedDown()` must return true for all `TableSource` instances returned from a `applyPredicate()` call.
+
+Attention In order for Flink to distinguish a filter push-down table source from its original form, `explainSource` method must be override to include information regarding the push-down filters.
+
+{% top %}
+
+### Defining a TableSource for Lookups
+
+Attention This is an experimental feature. The interface may be changed in future versions. It's only supported in Blink planner.
+
+The `LookupableTableSource` interface adds support for the table to be accessed via key column(s) in a lookup fashion. This is very useful when used to join with a dimension table to enrich some information. If you want to use the `TableSource` in lookup mode, you should use the source in [temporal table join syntax]({% link dev/table/streaming/joins.md %}).
+
+The interface looks as follows:
+
+
+
+{% highlight java %}
+LookupableTableSource implements TableSource {
+
+ public TableFunction getLookupFunction(String[] lookupkeys);
+
+ public AsyncTableFunction getAsyncLookupFunction(String[] lookupkeys);
+
+ public boolean isAsyncEnabled();
+}
+{% endhighlight %}
+
+
+* `getLookupFunction(lookupkeys)`: Returns a `TableFunction` which used to lookup the matched row(s) via lookup keys. The lookupkeys are the field names of `LookupableTableSource` in the join equal conditions. The eval method parameters of the returned `TableFunction`'s should be in the order which `lookupkeys` defined. It is recommended to define the parameters in varargs (e.g. `eval(Object... lookupkeys)` to match all the cases). The return type of the `TableFunction` must be identical to the return type defined by the `TableSource.getReturnType()` method.
+* `getAsyncLookupFunction(lookupkeys)`: Optional. Similar to `getLookupFunction`, but the `AsyncLookupFunction` lookups the matched row(s) asynchronously. The underlying of `AsyncLookupFunction` will be called via [Async I/O]({% link dev/stream/operators/asyncio.md %}). The first argument of the eval method of the returned `AsyncTableFunction` should be defined as `java.util.concurrent.CompletableFuture` to collect results asynchronously (e.g. `eval(CompletableFuture> result, Object... lookupkeys)`). The implementation of this method can throw an exception if the TableSource doesn't support asynchronously lookup.
+* `isAsyncEnabled()`: Returns true if async lookup is enabled. It requires `getAsyncLookupFunction(lookupkeys)` is implemented if `isAsyncEnabled` returns true.
+
+{% top %}
+
+Define a TableSink
+------------------
+
+A `TableSink` specifies how to emit a `Table` to an external system or location. The interface is generic such that it can support different storage locations and formats. There are different table sinks for batch tables and streaming tables.
+
+The general interface looks as follows:
+
+
+
+{% highlight java %}
+TableSink {
+
+ public TypeInformation getOutputType();
+
+ public String[] getFieldNames();
+
+ public TypeInformation[] getFieldTypes();
+
+ public TableSink configure(String[] fieldNames, TypeInformation[] fieldTypes);
+}
+{% endhighlight %}
+
+
+The `TableSink#configure` method is called to pass the schema of the Table (field names and types) to emit to the `TableSink`. The method must return a new instance of the TableSink which is configured to emit the provided Table schema. Note that the provided `TableSchema` shouldn't contain computed columns to reflect the schema of the physical `TableSink`.
+
+### BatchTableSink
+
+Defines an external `TableSink` to emit a batch table.
+
+The interface looks as follows:
+
+
+
+{% top %}
+
+### AppendStreamTableSink
+
+Defines an external `TableSink` to emit a streaming table with only insert changes.
+
+The interface looks as follows:
+
+
+
+If the table is also modified by update or delete changes, a `TableException` will be thrown.
+
+{% top %}
+
+### RetractStreamTableSink
+
+Defines an external `TableSink` to emit a streaming table with insert, update, and delete changes.
+
+The interface looks as follows:
+
+
+
+The table will be converted into a stream of accumulate and retraction messages which are encoded as Java `Tuple2`. The first field is a boolean flag to indicate the message type (`true` indicates insert, `false` indicates delete). The second field holds the record of the requested type `T`.
+
+{% top %}
+
+### UpsertStreamTableSink
+
+Defines an external `TableSink` to emit a streaming table with insert, update, and delete changes.
+
+The interface looks as follows:
+
+
+
+{% highlight java %}
+UpsertStreamTableSink implements TableSink> {
+
+ public void setKeyFields(String[] keys);
+
+ public void setIsAppendOnly(boolean isAppendOnly);
+
+ public TypeInformation getRecordType();
+
+ public DataStreamSink> consumeDataStream(DataStream> dataStream);
+}
+{% endhighlight %}
+
+
+The table must be have unique key fields (atomic or composite) or be append-only. If the table does not have a unique key and is not append-only, a `TableException` will be thrown. The unique key of the table is configured by the `UpsertStreamTableSink#setKeyFields()` method.
+
+The table will be converted into a stream of upsert and delete messages which are encoded as a Java `Tuple2`. The first field is a boolean flag to indicate the message type. The second field holds the record of the requested type `T`.
+
+A message with true boolean field is an upsert message for the configured key. A message with false flag is a delete message for the configured key. If the table is append-only, all messages will have a true flag and must be interpreted as insertions.
+
+{% top %}
+
+Define a TableFactory
+---------------------
+
+A `TableFactory` allows to create different table-related instances from string-based properties. All available factories are called for matching to the given set of properties and a corresponding factory class.
+
+Factories leverage Java's [Service Provider Interfaces (SPI)](https://docs.oracle.com/javase/tutorial/sound/SPI-intro.html) for discovering. This means that every dependency and JAR file should contain a file `org.apache.flink.table.factories.TableFactory` in the `META_INF/services` resource directory that lists all available table factories that it provides.
+
+Every table factory needs to implement the following interface:
+
+
+
+* `requiredContext()`: Specifies the context that this factory has been implemented for. The framework guarantees to only match for this factory if the specified set of properties and values are met. Typical properties might be `connector.type`, `format.type`, or `update-mode`. Property keys such as `connector.property-version` and `format.property-version` are reserved for future backwards compatibility cases.
+* `supportedProperties()`: List of property keys that this factory can handle. This method will be used for validation. If a property is passed that this factory cannot handle, an exception will be thrown. The list must not contain the keys that are specified by the context.
+
+In order to create a specific instance, a factory class can implement one or more interfaces provided in `org.apache.flink.table.factories`:
+
+* `BatchTableSourceFactory`: Creates a batch table source.
+* `BatchTableSinkFactory`: Creates a batch table sink.
+* `StreamTableSourceFactory`: Creates a stream table source.
+* `StreamTableSinkFactory`: Creates a stream table sink.
+* `DeserializationSchemaFactory`: Creates a deserialization schema format.
+* `SerializationSchemaFactory`: Creates a serialization schema format.
+
+The discovery of a factory happens in multiple stages:
+
+- Discover all available factories.
+- Filter by factory class (e.g., `StreamTableSourceFactory`).
+- Filter by matching context.
+- Filter by supported properties.
+- Verify that exactly one factory matches, otherwise throw an `AmbiguousTableFactoryException` or `NoMatchingTableFactoryException`.
+
+The following example shows how to provide a custom streaming source with an additional `connector.debug` property flag for parameterization.
+
+
+
+{% highlight java %}
+import org.apache.flink.table.sources.StreamTableSource;
+import org.apache.flink.types.Row;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+class MySystemTableSourceFactory implements StreamTableSourceFactory {
+
+ @Override
+ public Map requiredContext() {
+ Map context = new HashMap<>();
+ context.put("update-mode", "append");
+ context.put("connector.type", "my-system");
+ return context;
+ }
+
+ @Override
+ public List supportedProperties() {
+ List list = new ArrayList<>();
+ list.add("connector.debug");
+ return list;
+ }
+
+ @Override
+ public StreamTableSource createStreamTableSource(Map properties) {
+ boolean isDebug = Boolean.valueOf(properties.get("connector.debug"));
+
+ # additional validation of the passed properties can also happen here
+
+ return new MySystemAppendTableSource(isDebug);
+ }
+}
+{% endhighlight %}
+
+
+
+{% highlight scala %}
+import java.util
+import org.apache.flink.table.sources.StreamTableSource
+import org.apache.flink.types.Row
+
+class MySystemTableSourceFactory extends StreamTableSourceFactory[Row] {
+
+ override def requiredContext(): util.Map[String, String] = {
+ val context = new util.HashMap[String, String]()
+ context.put("update-mode", "append")
+ context.put("connector.type", "my-system")
+ context
+ }
+
+ override def supportedProperties(): util.List[String] = {
+ val properties = new util.ArrayList[String]()
+ properties.add("connector.debug")
+ properties
+ }
+
+ override def createStreamTableSource(properties: util.Map[String, String]): StreamTableSource[Row] = {
+ val isDebug = java.lang.Boolean.valueOf(properties.get("connector.debug"))
+
+ # additional validation of the passed properties can also happen here
+
+ new MySystemAppendTableSource(isDebug)
+ }
+}
+{% endhighlight %}
+
+
+
+{% top %}
+
+### Use a TableFactory in the SQL Client
+
+In a SQL Client environment file, the previously presented factory could be declared as:
+
+{% highlight yaml %}
+tables:
+ - name: MySystemTable
+ type: source
+ update-mode: append
+ connector:
+ type: my-system
+ debug: true
+{% endhighlight %}
+
+The YAML file is translated into flattened string properties and a table factory is called with those properties that describe the connection to the external system:
+
+{% highlight text %}
+update-mode=append
+connector.type=my-system
+connector.debug=true
+{% endhighlight %}
+
+Attention Properties such as `tables.#.name` or `tables.#.type` are SQL Client specifics and are not passed to any factory. The `type` property decides, depending on the execution environment, whether a `BatchTableSourceFactory`/`StreamTableSourceFactory` (for `source`), a `BatchTableSinkFactory`/`StreamTableSinkFactory` (for `sink`), or both (for `both`) need to discovered.
+
+{% top %}
+
+### Use a TableFactory in the Table & SQL API
+
+For a type-safe, programmatic approach with explanatory Scaladoc/Javadoc, the Table & SQL API offers descriptors in `org.apache.flink.table.descriptors` that translate into string-based properties. See the [built-in descriptors]({% link dev/table/connect.md %}) for sources, sinks, and formats as a reference.
+
+
+
+
+A custom descriptor can be defined by extending the `ConnectorDescriptor` class.
+
+{% highlight java %}
+import org.apache.flink.table.descriptors.ConnectorDescriptor;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Connector to MySystem with debug mode.
+ */
+public class MySystemConnector extends ConnectorDescriptor {
+
+ public final boolean isDebug;
+
+ public MySystemConnector(boolean isDebug) {
+ super("my-system", 1, false);
+ this.isDebug = isDebug;
+ }
+
+ @Override
+ protected Map toConnectorProperties() {
+ Map properties = new HashMap<>();
+ properties.put("connector.debug", Boolean.toString(isDebug));
+ return properties;
+ }
+}
+{% endhighlight %}
+
+The descriptor can then be used to create a table with the table environment.
+
+{% highlight java %}
+StreamTableEnvironment tableEnv = // ...
+
+tableEnv
+ .connect(new MySystemConnector(true))
+ .withSchema(...)
+ .inAppendMode()
+ .createTemporaryTable("MySystemTable");
+{% endhighlight %}
+
+
+
+
+
+A custom descriptor can be defined by extending the `ConnectorDescriptor` class.
+
+{% highlight scala %}
+import org.apache.flink.table.descriptors.ConnectorDescriptor
+import java.util.HashMap
+import java.util.Map
+
+/**
+ * Connector to MySystem with debug mode.
+ */
+class MySystemConnector(isDebug: Boolean) extends ConnectorDescriptor("my-system", 1, false) {
+
+ override protected def toConnectorProperties(): Map[String, String] = {
+ val properties = new HashMap[String, String]
+ properties.put("connector.debug", isDebug.toString)
+ properties
+ }
+}
+{% endhighlight %}
+
+The descriptor can then be used to create a table with the table environment.
+
+{% highlight scala %}
+val tableEnv: StreamTableEnvironment = // ...
+
+tableEnv
+ .connect(new MySystemConnector(isDebug = true))
+ .withSchema(...)
+ .inAppendMode()
+ .createTemporaryTable("MySystemTable")
+{% endhighlight %}
+
+
+
+
+
+You can use a Java `TableFactory` from Python using the `CustomConnectorDescriptor`.
+
+{% highlight python %}
+s_env = StreamExecutionEnvironment.get_execution_environment()
+st_env = StreamTableEnvironment.create(s_env)
+
+custom_connector = CustomConnectorDescriptor('my-system', 1, False)
+st_env\
+ .connect(custom_connector.property("connector.debug", "true")) \
+ .with_schema(...) \
+ .in_append_mode()\
+ .create_temporary_table("MySystemTable")
+{% endhighlight %}
+
+
+
+
+{% top %}
diff --git a/docs/dev/table/legacySourceSinks.zh.md b/docs/dev/table/legacySourceSinks.zh.md
new file mode 100644
index 00000000000000..a410594654fbc6
--- /dev/null
+++ b/docs/dev/table/legacySourceSinks.zh.md
@@ -0,0 +1,815 @@
+---
+title: "Legacy Table Source & Sink Interfaces"
+---
+
+
+A `TableSource` provides access to data which is stored in external systems (database, key-value store, message queue) or files. After a [TableSource is registered in a TableEnvironment]({% link dev/table/common.zh.md %}#register-a-tablesource) it can be accessed by [Table API]({% link dev/table/tableApi.zh.md %}) or [SQL]({% link dev/table/sql/queries.zh.md %}) queries.
+
+A `TableSink` [emits a Table]({% link dev/table/common.zh.md %}#emit-a-table) to an external storage system, such as a database, key-value store, message queue, or file system (in different encodings, e.g., CSV, Parquet, or ORC).
+
+A `TableFactory` allows for separating the declaration of a connection to an external system from the actual implementation. A table factory creates configured instances of table sources and sinks from normalized, string-based properties. The properties can be generated programmatically using a `Descriptor` or via YAML configuration files for the [SQL Client]({% link dev/table/sqlClient.zh.md %}).
+
+Have a look at the [common concepts and API]({% link dev/table/common.zh.md %}) page for details how to [register a TableSource]({% link dev/table/common.zh.md %}#register-a-tablesource) and how to [emit a Table through a TableSink]({% link dev/table/common.zh.md %}#emit-a-table). See the [built-in sources, sinks, and formats]({% link dev/table/connect.zh.md %}) page for examples how to use factories.
+
+* This will be replaced by the TOC
+{:toc}
+
+Define a TableSource
+--------------------
+
+A `TableSource` is a generic interface that gives Table API and SQL queries access to data stored in an external system. It provides the schema of the table and the records that are mapped to rows with the table's schema. Depending on whether the `TableSource` is used in a streaming or batch query, the records are produced as a `DataSet` or `DataStream`.
+
+If a `TableSource` is used in a streaming query it must implement the `StreamTableSource` interface, if it is used in a batch query it must implement the `BatchTableSource` interface. A `TableSource` can also implement both interfaces and be used in streaming and batch queries.
+
+`StreamTableSource` and `BatchTableSource` extend the base interface `TableSource` that defines the following methods:
+
+
+
+{% highlight java %}
+TableSource {
+
+ public TableSchema getTableSchema();
+
+ public TypeInformation getReturnType();
+
+ public String explainSource();
+}
+{% endhighlight %}
+
+
+* `getTableSchema()`: Returns the schema of the produced table, i.e., the names and types of the fields of the table. The field types are defined using Flink's `DataType` (see [Table API types]({% link dev/table/types.zh.md %}) and [SQL types]({% link dev/table/sql/index.zh.md %}#data-types)). Note that the returned `TableSchema` shouldn't contain computed columns to reflect the schema of the physical `TableSource`.
+
+* `getReturnType()`: Returns the physical type of the `DataStream` (`StreamTableSource`) or `DataSet` (`BatchTableSource`) and the records that are produced by the `TableSource`.
+
+* `explainSource()`: Returns a String that describes the `TableSource`. This method is optional and used for display purposes only.
+
+The `TableSource` interface separates the logical table schema from the physical type of the returned `DataStream` or `DataSet`. As a consequence, all fields of the table schema (`getTableSchema()`) must be mapped to a field with corresponding type of the physical return type (`getReturnType()`). By default, this mapping is done based on field names. For example, a `TableSource` that defines a table schema with two fields `[name: String, size: Integer]` requires a `TypeInformation` with at least two fields called `name` and `size` of type `String` and `Integer`, respectively. This could be a `PojoTypeInfo` or a `RowTypeInfo` that have two fields named `name` and `size` with matching types.
+
+However, some types, such as Tuple or CaseClass types, do support custom field names. If a `TableSource` returns a `DataStream` or `DataSet` of a type with fixed field names, it can implement the `DefinedFieldMapping` interface to map field names from the table schema to field names of the physical return type.
+
+### Defining a BatchTableSource
+
+The `BatchTableSource` interface extends the `TableSource` interface and defines one additional method:
+
+
+
+* `getDataSet(execEnv)`: Returns a `DataSet` with the data of the table. The type of the `DataSet` must be identical to the return type defined by the `TableSource.getReturnType()` method. The `DataSet` can by created using a regular [data source]({% link dev/batch/index.zh.md %}#data-sources) of the DataSet API. Commonly, a `BatchTableSource` is implemented by wrapping a `InputFormat` or [batch connector]({% link dev/batch/connectors.zh.md %}).
+
+{% top %}
+
+### Defining a StreamTableSource
+
+The `StreamTableSource` interface extends the `TableSource` interface and defines one additional method:
+
+
+
+* `getDataStream(execEnv)`: Returns a `DataStream` with the data of the table. The type of the `DataStream` must be identical to the return type defined by the `TableSource.getReturnType()` method. The `DataStream` can by created using a regular [data source]({% link dev/datastream_api.zh.md %}#data-sources) of the DataStream API. Commonly, a `StreamTableSource` is implemented by wrapping a `SourceFunction` or a [stream connector]({% link dev/connectors/index.zh.md %}).
+
+{% top %}
+
+### Defining a TableSource with Time Attributes
+
+Time-based operations of streaming [Table API]({% link dev/table/tableApi.zh.md %}#group-windows) and [SQL]({% link dev/table/sql/queries.zh.md %}#group-windows) queries, such as windowed aggregations or joins, require explicitly specified [time attributes]({% link dev/table/streaming/time_attributes.zh.md %}).
+
+A `TableSource` defines a time attribute as a field of type `Types.SQL_TIMESTAMP` in its table schema. In contrast to all regular fields in the schema, a time attribute must not be matched to a physical field in the return type of the table source. Instead, a `TableSource` defines a time attribute by implementing a certain interface.
+
+#### Defining a Processing Time Attribute
+
+[Processing time attributes]({% link dev/table/streaming/time_attributes.zh.md %}#processing-time) are commonly used in streaming queries. A processing time attribute returns the current wall-clock time of the operator that accesses it. A `TableSource` defines a processing time attribute by implementing the `DefinedProctimeAttribute` interface. The interface looks as follows:
+
+
+
+* `getProctimeAttribute()`: Returns the name of the processing time attribute. The specified attribute must be defined of type `Types.SQL_TIMESTAMP` in the table schema and can be used in time-based operations. A `DefinedProctimeAttribute` table source can define no processing time attribute by returning `null`.
+
+Attention Both `StreamTableSource` and `BatchTableSource` can implement `DefinedProctimeAttribute` and define a processing time attribute. In case of a `BatchTableSource` the processing time field is initialized with the current timestamp during the table scan.
+
+#### Defining a Rowtime Attribute
+
+[Rowtime attributes]({% link dev/table/streaming/time_attributes.zh.md %}#event-time) are attributes of type `TIMESTAMP` and handled in a unified way in stream and batch queries.
+
+A table schema field of type `SQL_TIMESTAMP` can be declared as rowtime attribute by specifying
+
+* the name of the field,
+* a `TimestampExtractor` that computes the actual value for the attribute (usually from one or more other fields), and
+* a `WatermarkStrategy` that specifies how watermarks are generated for the the rowtime attribute.
+
+A `TableSource` defines a rowtime attribute by implementing the `DefinedRowtimeAttributes` interface. The interface looks as follows:
+
+
+
+{% highlight java %}
+DefinedRowtimeAttribute {
+
+ public List getRowtimeAttributeDescriptors();
+}
+{% endhighlight %}
+
+
+* `getRowtimeAttributeDescriptors()`: Returns a list of `RowtimeAttributeDescriptor`. A `RowtimeAttributeDescriptor` describes a rowtime attribute with the following properties:
+ * `attributeName`: The name of the rowtime attribute in the table schema. The field must be defined with type `Types.SQL_TIMESTAMP`.
+ * `timestampExtractor`: The timestamp extractor extracts the timestamp from a record with the return type. For example, it can convert a Long field into a timestamp or parse a String-encoded timestamp. Flink comes with a set of built-in `TimestampExtractor` implementation for common use cases. It is also possible to provide a custom implementation.
+ * `watermarkStrategy`: The watermark strategy defines how watermarks are generated for the rowtime attribute. Flink comes with a set of built-in `WatermarkStrategy` implementations for common use cases. It is also possible to provide a custom implementation.
+
+Attention Although the `getRowtimeAttributeDescriptors()` method returns a list of descriptors, only a single rowtime attribute is support at the moment. We plan to remove this restriction in the future and support tables with more than one rowtime attribute.
+
+Attention Both, `StreamTableSource` and `BatchTableSource`, can implement `DefinedRowtimeAttributes` and define a rowtime attribute. In either case, the rowtime field is extracted using the `TimestampExtractor`. Hence, a `TableSource` that implements `StreamTableSource` and `BatchTableSource` and defines a rowtime attribute provides exactly the same data to streaming and batch queries.
+
+##### Provided Timestamp Extractors
+
+Flink provides `TimestampExtractor` implementations for common use cases.
+
+The following `TimestampExtractor` implementations are currently available:
+
+* `ExistingField(fieldName)`: Extracts the value of a rowtime attribute from an existing `LONG`, `SQL_TIMESTAMP`, or timestamp formatted `STRING` field. One example of such a string would be '2018-05-28 12:34:56.000'.
+* `StreamRecordTimestamp()`: Extracts the value of a rowtime attribute from the timestamp of the `DataStream` `StreamRecord`. Note, this `TimestampExtractor` is not available for batch table sources.
+
+A custom `TimestampExtractor` can be defined by implementing the corresponding interface.
+
+##### Provided Watermark Strategies
+
+Flink provides `WatermarkStrategy` implementations for common use cases.
+
+The following `WatermarkStrategy` implementations are currently available:
+
+* `AscendingTimestamps`: A watermark strategy for ascending timestamps. Records with timestamps that are out-of-order will be considered late.
+* `BoundedOutOfOrderTimestamps(delay)`: A watermark strategy for timestamps that are at most out-of-order by the specified delay.
+* `PreserveWatermarks()`: A strategy which indicates the watermarks should be preserved from the underlying `DataStream`.
+
+A custom `WatermarkStrategy` can be defined by implementing the corresponding interface.
+
+{% top %}
+
+### Defining a TableSource with Projection Push-Down
+
+A `TableSource` supports projection push-down by implementing the `ProjectableTableSource` interface. The interface defines a single method:
+
+
+
+* `projectFields(fields)`: Returns a *copy* of the `TableSource` with adjusted physical return type. The `fields` parameter provides the indexes of the fields that must be provided by the `TableSource`. The indexes relate to the `TypeInformation` of the physical return type, *not* to the logical table schema. The copied `TableSource` must adjust its return type and the returned `DataStream` or `DataSet`. The `TableSchema` of the copied `TableSource` must not be changed, i.e, it must be the same as the original `TableSource`. If the `TableSource` implements the `DefinedFieldMapping` interface, the field mapping must be adjusted to the new return type.
+
+Attention In order for Flink to distinguish a projection push-down table source from its original form, `explainSource` method must be override to include information regarding the projected fields.
+
+The `ProjectableTableSource` adds support to project flat fields. If the `TableSource` defines a table with nested schema, it can implement the `NestedFieldsProjectableTableSource` to extend the projection to nested fields. The `NestedFieldsProjectableTableSource` is defined as follows:
+
+
+
+* `projectNestedField(fields, nestedFields)`: Returns a *copy* of the `TableSource` with adjusted physical return type. Fields of the physical return type may be removed or reordered but their type must not be changed. The contract of this method is essentially the same as for the `ProjectableTableSource.projectFields()` method. In addition, the `nestedFields` parameter contains for each field index in the `fields` list, a list of paths to all nested fields that are accessed by the query. All other nested fields do not need to be read, parsed, and set in the records that are produced by the `TableSource`.
+
+Attention the types of the projected fields must not be changed but unused fields may be set to null or to a default value.
+
+{% top %}
+
+### Defining a TableSource with Filter Push-Down
+
+The `FilterableTableSource` interface adds support for filter push-down to a `TableSource`. A `TableSource` extending this interface is able to filter records such that the returned `DataStream` or `DataSet` returns fewer records.
+
+The interface looks as follows:
+
+
+
+* `applyPredicate(predicates)`: Returns a *copy* of the `TableSource` with added predicates. The `predicates` parameter is a mutable list of conjunctive predicates that are "offered" to the `TableSource`. The `TableSource` accepts to evaluate a predicate by removing it from the list. Predicates that are left in the list will be evaluated by a subsequent filter operator.
+* `isFilterPushedDown()`: Returns true if the `applyPredicate()` method was called before. Hence, `isFilterPushedDown()` must return true for all `TableSource` instances returned from a `applyPredicate()` call.
+
+Attention In order for Flink to distinguish a filter push-down table source from its original form, `explainSource` method must be override to include information regarding the push-down filters.
+
+{% top %}
+
+### Defining a TableSource for Lookups
+
+Attention This is an experimental feature. The interface may be changed in future versions. It's only supported in Blink planner.
+
+The `LookupableTableSource` interface adds support for the table to be accessed via key column(s) in a lookup fashion. This is very useful when used to join with a dimension table to enrich some information. If you want to use the `TableSource` in lookup mode, you should use the source in [temporal table join syntax]({% link dev/table/streaming/joins.zh.md %}).
+
+The interface looks as follows:
+
+
+
+{% highlight java %}
+LookupableTableSource implements TableSource {
+
+ public TableFunction getLookupFunction(String[] lookupkeys);
+
+ public AsyncTableFunction getAsyncLookupFunction(String[] lookupkeys);
+
+ public boolean isAsyncEnabled();
+}
+{% endhighlight %}
+
+
+* `getLookupFunction(lookupkeys)`: Returns a `TableFunction` which used to lookup the matched row(s) via lookup keys. The lookupkeys are the field names of `LookupableTableSource` in the join equal conditions. The eval method parameters of the returned `TableFunction`'s should be in the order which `lookupkeys` defined. It is recommended to define the parameters in varargs (e.g. `eval(Object... lookupkeys)` to match all the cases). The return type of the `TableFunction` must be identical to the return type defined by the `TableSource.getReturnType()` method.
+* `getAsyncLookupFunction(lookupkeys)`: Optional. Similar to `getLookupFunction`, but the `AsyncLookupFunction` lookups the matched row(s) asynchronously. The underlying of `AsyncLookupFunction` will be called via [Async I/O]({% link dev/stream/operators/asyncio.zh.md %}). The first argument of the eval method of the returned `AsyncTableFunction` should be defined as `java.util.concurrent.CompletableFuture` to collect results asynchronously (e.g. `eval(CompletableFuture> result, Object... lookupkeys)`). The implementation of this method can throw an exception if the TableSource doesn't support asynchronously lookup.
+* `isAsyncEnabled()`: Returns true if async lookup is enabled. It requires `getAsyncLookupFunction(lookupkeys)` is implemented if `isAsyncEnabled` returns true.
+
+{% top %}
+
+Define a TableSink
+------------------
+
+A `TableSink` specifies how to emit a `Table` to an external system or location. The interface is generic such that it can support different storage locations and formats. There are different table sinks for batch tables and streaming tables.
+
+The general interface looks as follows:
+
+
+
+{% highlight java %}
+TableSink {
+
+ public TypeInformation getOutputType();
+
+ public String[] getFieldNames();
+
+ public TypeInformation[] getFieldTypes();
+
+ public TableSink configure(String[] fieldNames, TypeInformation[] fieldTypes);
+}
+{% endhighlight %}
+
+
+The `TableSink#configure` method is called to pass the schema of the Table (field names and types) to emit to the `TableSink`. The method must return a new instance of the TableSink which is configured to emit the provided Table schema. Note that the provided `TableSchema` shouldn't contain computed columns to reflect the schema of the physical `TableSink`.
+
+### BatchTableSink
+
+Defines an external `TableSink` to emit a batch table.
+
+The interface looks as follows:
+
+
+
+{% top %}
+
+### AppendStreamTableSink
+
+Defines an external `TableSink` to emit a streaming table with only insert changes.
+
+The interface looks as follows:
+
+
+
+If the table is also modified by update or delete changes, a `TableException` will be thrown.
+
+{% top %}
+
+### RetractStreamTableSink
+
+Defines an external `TableSink` to emit a streaming table with insert, update, and delete changes.
+
+The interface looks as follows:
+
+
+
+The table will be converted into a stream of accumulate and retraction messages which are encoded as Java `Tuple2`. The first field is a boolean flag to indicate the message type (`true` indicates insert, `false` indicates delete). The second field holds the record of the requested type `T`.
+
+{% top %}
+
+### UpsertStreamTableSink
+
+Defines an external `TableSink` to emit a streaming table with insert, update, and delete changes.
+
+The interface looks as follows:
+
+
+
+{% highlight java %}
+UpsertStreamTableSink implements TableSink> {
+
+ public void setKeyFields(String[] keys);
+
+ public void setIsAppendOnly(boolean isAppendOnly);
+
+ public TypeInformation getRecordType();
+
+ public DataStreamSink> consumeDataStream(DataStream> dataStream);
+}
+{% endhighlight %}
+
+
+The table must be have unique key fields (atomic or composite) or be append-only. If the table does not have a unique key and is not append-only, a `TableException` will be thrown. The unique key of the table is configured by the `UpsertStreamTableSink#setKeyFields()` method.
+
+The table will be converted into a stream of upsert and delete messages which are encoded as a Java `Tuple2`. The first field is a boolean flag to indicate the message type. The second field holds the record of the requested type `T`.
+
+A message with true boolean field is an upsert message for the configured key. A message with false flag is a delete message for the configured key. If the table is append-only, all messages will have a true flag and must be interpreted as insertions.
+
+{% top %}
+
+Define a TableFactory
+---------------------
+
+A `TableFactory` allows to create different table-related instances from string-based properties. All available factories are called for matching to the given set of properties and a corresponding factory class.
+
+Factories leverage Java's [Service Provider Interfaces (SPI)](https://docs.oracle.com/javase/tutorial/sound/SPI-intro.html) for discovering. This means that every dependency and JAR file should contain a file `org.apache.flink.table.factories.TableFactory` in the `META_INF/services` resource directory that lists all available table factories that it provides.
+
+Every table factory needs to implement the following interface:
+
+
+
+* `requiredContext()`: Specifies the context that this factory has been implemented for. The framework guarantees to only match for this factory if the specified set of properties and values are met. Typical properties might be `connector.type`, `format.type`, or `update-mode`. Property keys such as `connector.property-version` and `format.property-version` are reserved for future backwards compatibility cases.
+* `supportedProperties()`: List of property keys that this factory can handle. This method will be used for validation. If a property is passed that this factory cannot handle, an exception will be thrown. The list must not contain the keys that are specified by the context.
+
+In order to create a specific instance, a factory class can implement one or more interfaces provided in `org.apache.flink.table.factories`:
+
+* `BatchTableSourceFactory`: Creates a batch table source.
+* `BatchTableSinkFactory`: Creates a batch table sink.
+* `StreamTableSourceFactory`: Creates a stream table source.
+* `StreamTableSinkFactory`: Creates a stream table sink.
+* `DeserializationSchemaFactory`: Creates a deserialization schema format.
+* `SerializationSchemaFactory`: Creates a serialization schema format.
+
+The discovery of a factory happens in multiple stages:
+
+- Discover all available factories.
+- Filter by factory class (e.g., `StreamTableSourceFactory`).
+- Filter by matching context.
+- Filter by supported properties.
+- Verify that exactly one factory matches, otherwise throw an `AmbiguousTableFactoryException` or `NoMatchingTableFactoryException`.
+
+The following example shows how to provide a custom streaming source with an additional `connector.debug` property flag for parameterization.
+
+
+
+{% highlight java %}
+import org.apache.flink.table.sources.StreamTableSource;
+import org.apache.flink.types.Row;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+class MySystemTableSourceFactory implements StreamTableSourceFactory {
+
+ @Override
+ public Map requiredContext() {
+ Map context = new HashMap<>();
+ context.put("update-mode", "append");
+ context.put("connector.type", "my-system");
+ return context;
+ }
+
+ @Override
+ public List supportedProperties() {
+ List list = new ArrayList<>();
+ list.add("connector.debug");
+ return list;
+ }
+
+ @Override
+ public StreamTableSource createStreamTableSource(Map properties) {
+ boolean isDebug = Boolean.valueOf(properties.get("connector.debug"));
+
+ # additional validation of the passed properties can also happen here
+
+ return new MySystemAppendTableSource(isDebug);
+ }
+}
+{% endhighlight %}
+
+
+
+{% highlight scala %}
+import java.util
+import org.apache.flink.table.sources.StreamTableSource
+import org.apache.flink.types.Row
+
+class MySystemTableSourceFactory extends StreamTableSourceFactory[Row] {
+
+ override def requiredContext(): util.Map[String, String] = {
+ val context = new util.HashMap[String, String]()
+ context.put("update-mode", "append")
+ context.put("connector.type", "my-system")
+ context
+ }
+
+ override def supportedProperties(): util.List[String] = {
+ val properties = new util.ArrayList[String]()
+ properties.add("connector.debug")
+ properties
+ }
+
+ override def createStreamTableSource(properties: util.Map[String, String]): StreamTableSource[Row] = {
+ val isDebug = java.lang.Boolean.valueOf(properties.get("connector.debug"))
+
+ # additional validation of the passed properties can also happen here
+
+ new MySystemAppendTableSource(isDebug)
+ }
+}
+{% endhighlight %}
+
+
+
+{% top %}
+
+### Use a TableFactory in the SQL Client
+
+In a SQL Client environment file, the previously presented factory could be declared as:
+
+{% highlight yaml %}
+tables:
+ - name: MySystemTable
+ type: source
+ update-mode: append
+ connector:
+ type: my-system
+ debug: true
+{% endhighlight %}
+
+The YAML file is translated into flattened string properties and a table factory is called with those properties that describe the connection to the external system:
+
+{% highlight text %}
+update-mode=append
+connector.type=my-system
+connector.debug=true
+{% endhighlight %}
+
+Attention Properties such as `tables.#.name` or `tables.#.type` are SQL Client specifics and are not passed to any factory. The `type` property decides, depending on the execution environment, whether a `BatchTableSourceFactory`/`StreamTableSourceFactory` (for `source`), a `BatchTableSinkFactory`/`StreamTableSinkFactory` (for `sink`), or both (for `both`) need to discovered.
+
+{% top %}
+
+### Use a TableFactory in the Table & SQL API
+
+For a type-safe, programmatic approach with explanatory Scaladoc/Javadoc, the Table & SQL API offers descriptors in `org.apache.flink.table.descriptors` that translate into string-based properties. See the [built-in descriptors]({% link dev/table/connect.zh.md %}) for sources, sinks, and formats as a reference.
+
+
+
+
+A custom descriptor can be defined by extending the `ConnectorDescriptor` class.
+
+{% highlight java %}
+import org.apache.flink.table.descriptors.ConnectorDescriptor;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Connector to MySystem with debug mode.
+ */
+public class MySystemConnector extends ConnectorDescriptor {
+
+ public final boolean isDebug;
+
+ public MySystemConnector(boolean isDebug) {
+ super("my-system", 1, false);
+ this.isDebug = isDebug;
+ }
+
+ @Override
+ protected Map toConnectorProperties() {
+ Map properties = new HashMap<>();
+ properties.put("connector.debug", Boolean.toString(isDebug));
+ return properties;
+ }
+}
+{% endhighlight %}
+
+The descriptor can then be used to create a table with the table environment.
+
+{% highlight java %}
+StreamTableEnvironment tableEnv = // ...
+
+tableEnv
+ .connect(new MySystemConnector(true))
+ .withSchema(...)
+ .inAppendMode()
+ .createTemporaryTable("MySystemTable");
+{% endhighlight %}
+
+
+
+
+
+A custom descriptor can be defined by extending the `ConnectorDescriptor` class.
+
+{% highlight scala %}
+import org.apache.flink.table.descriptors.ConnectorDescriptor
+import java.util.HashMap
+import java.util.Map
+
+/**
+ * Connector to MySystem with debug mode.
+ */
+class MySystemConnector(isDebug: Boolean) extends ConnectorDescriptor("my-system", 1, false) {
+
+ override protected def toConnectorProperties(): Map[String, String] = {
+ val properties = new HashMap[String, String]
+ properties.put("connector.debug", isDebug.toString)
+ properties
+ }
+}
+{% endhighlight %}
+
+The descriptor can then be used to create a table with the table environment.
+
+{% highlight scala %}
+val tableEnv: StreamTableEnvironment = // ...
+
+tableEnv
+ .connect(new MySystemConnector(isDebug = true))
+ .withSchema(...)
+ .inAppendMode()
+ .createTemporaryTable("MySystemTable")
+{% endhighlight %}
+
+
+
+
+
+You can use a Java `TableFactory` from Python using the `CustomConnectorDescriptor`.
+
+{% highlight python %}
+s_env = StreamExecutionEnvironment.get_execution_environment()
+st_env = StreamTableEnvironment.create(s_env)
+
+custom_connector = CustomConnectorDescriptor('my-system', 1, False)
+st_env\
+ .connect(custom_connector.property("connector.debug", "true")) \
+ .with_schema(...) \
+ .in_append_mode()\
+ .create_temporary_table("MySystemTable")
+{% endhighlight %}
+
+
+
+
+{% top %}
diff --git a/docs/dev/table/python/common_questions.md b/docs/dev/table/python/common_questions.md
index 89da24cbb5855f..de6c75e5c2243b 100644
--- a/docs/dev/table/python/common_questions.md
+++ b/docs/dev/table/python/common_questions.md
@@ -34,9 +34,9 @@ You can specify the version parameter to generate a Python virtual environment r
{% highlight bash %}
{% if site.is_stable %}
-$ setup-pyflink-virtual-env.sh {{ site.version }}
+$ sh setup-pyflink-virtual-env.sh {{ site.version }}
{% else %}
-$ setup-pyflink-virtual-env.sh
+$ sh setup-pyflink-virtual-env.sh
{% endif %}
{% endhighlight bash %}
@@ -69,11 +69,11 @@ A PyFlink job may depend on jar files, i.e. connectors, Java UDFs, etc.
You can specify the dependencies with the following Python Table APIs or through command-line arguments directly when submitting the job.
{% highlight python %}
-# NOTE: Only local file URLs (start with "file://") are supported.
-table_env.get_config().set_configuration("pipeline.jars", "file:///my/jar/path/connector.jar;file:///my/jar/path/udf.jar")
+# NOTE: Only local file URLs (start with "file:") are supported.
+table_env.get_config().get_configuration().set_string("pipeline.jars", "file:///my/jar/path/connector.jar;file:///my/jar/path/udf.jar")
-# NOTE: The Paths must specify a protocol (e.g. file://) and users should ensure that the URLs are accessible on both the client and the cluster.
-table_env.get_config().set_configuration("pipeline.classpaths", "file:///my/jar/path/connector.jar;file:///my/jar/path/udf.jar")
+# NOTE: The Paths must specify a protocol (e.g. "file") and users should ensure that the URLs are accessible on both the client and the cluster.
+table_env.get_config().get_configuration().set_string("pipeline.classpaths", "file:///my/jar/path/connector.jar;file:///my/jar/path/udf.jar")
{% endhighlight %}
For details about the APIs of adding Java dependency, you can refer to [the relevant documentation]({{ site.baseurl }}/dev/table/python/dependency_management.html##java-dependency)
diff --git a/docs/dev/table/python/common_questions.zh.md b/docs/dev/table/python/common_questions.zh.md
index f11f83c2175e78..3821c214fffc84 100644
--- a/docs/dev/table/python/common_questions.zh.md
+++ b/docs/dev/table/python/common_questions.zh.md
@@ -34,9 +34,9 @@ You can specify the version parameter to generate a Python virtual environment r
{% highlight bash %}
{% if site.is_stable %}
-$ setup-pyflink-virtual-env.sh {{ site.version }}
+$ sh setup-pyflink-virtual-env.sh {{ site.version }}
{% else %}
-$ setup-pyflink-virtual-env.sh
+$ sh setup-pyflink-virtual-env.sh
{% endif %}
{% endhighlight bash %}
@@ -69,11 +69,11 @@ A PyFlink job may depend on jar files, i.e. connectors, Java UDFs, etc.
You can specify the dependencies with the following Python Table APIs or through command-line arguments directly when submitting the job.
{% highlight python %}
-# NOTE: Only local file URLs (start with "file://") are supported.
-table_env.get_config().set_configuration("pipeline.jars", "file:///my/jar/path/connector.jar;file:///my/jar/path/udf.jar")
+# NOTE: Only local file URLs (start with "file:") are supported.
+table_env.get_config().get_configuration().set_string("pipeline.jars", "file:///my/jar/path/connector.jar;file:///my/jar/path/udf.jar")
-# NOTE: The Paths must specify a protocol (e.g. file://) and users should ensure that the URLs are accessible on both the client and the cluster.
-table_env.get_config().set_configuration("pipeline.classpaths", "file:///my/jar/path/connector.jar;file:///my/jar/path/udf.jar")
+# NOTE: The Paths must specify a protocol (e.g. "file") and users should ensure that the URLs are accessible on both the client and the cluster.
+table_env.get_config().get_configuration().set_string("pipeline.classpaths", "file:///my/jar/path/connector.jar;file:///my/jar/path/udf.jar")
{% endhighlight %}
For details about the APIs of adding Java dependency, you can refer to [the relevant documentation]({{ site.baseurl }}/zh/dev/table/python/dependency_management.html##java-dependency)
diff --git a/docs/dev/table/python/installation.md b/docs/dev/table/python/installation.md
index 0010ca989d8ef1..c33d83c56308ba 100644
--- a/docs/dev/table/python/installation.md
+++ b/docs/dev/table/python/installation.md
@@ -42,3 +42,5 @@ $ python -m pip install apache-flink
{% endhighlight %}
You can also build PyFlink from source by following the [development guide]({{ site.baseurl }}/flinkDev/building.html#build-pyflink).
+
+Note Starting from Flink 1.11, it's also supported to run PyFlink jobs locally on Windows and so you could develop and debug PyFlink jobs on Windows.
diff --git a/docs/dev/table/python/installation.zh.md b/docs/dev/table/python/installation.zh.md
index 43ccca8967b972..6e4e45f8d9f10e 100644
--- a/docs/dev/table/python/installation.zh.md
+++ b/docs/dev/table/python/installation.zh.md
@@ -25,20 +25,22 @@ under the License.
* This will be replaced by the TOC
{:toc}
-## Environment Requirements
-Note Python version (3.5, 3.6 or 3.7) is required for PyFlink. Please run the following command to make sure that it meets the requirements:
+## 环境要求
+注意 PyFlink 需要特定的Python 版本(3.5, 3.6 或 3.7)。请运行如下的命令确保版本满足要求。
{% highlight bash %}
$ python --version
# the version printed here must be 3.5, 3.6 or 3.7
{% endhighlight %}
-## Installation of PyFlink
+## PyFlink 安装
-PyFlink has already been deployed to PyPi and can be installed as following:
+PyFlink 已经被部署到 PyPi,可以按如下方式安装:
{% highlight bash %}
$ python -m pip install apache-flink
{% endhighlight %}
-You can also build PyFlink from source by following the [development guide]({{ site.baseurl }}/zh/flinkDev/building.html#build-pyflink).
+你也可以从源码手动构建 PyFlink,具体可以参见[开发指南]({{ site.baseurl }}/zh/flinkDev/building.html#build-pyflink).
+
+注意 从 Flink 1.11 版本开始, PyFlink 作业可以支持在 Windows 系统上本地运行,因此你也可以在 Windows 上开发和调试 PyFlink 作业了。
diff --git a/docs/dev/table/python/python_udfs.md b/docs/dev/table/python/python_udfs.md
index ea01cd0ca3faba..98ce5f064f2af7 100644
--- a/docs/dev/table/python/python_udfs.md
+++ b/docs/dev/table/python/python_udfs.md
@@ -48,6 +48,9 @@ class HashCode(ScalarFunction):
table_env = BatchTableEnvironment.create(env)
+# configure the off-heap memory of current taskmanager to enable the python worker uses off-heap memory.
+table_env.get_config().get_configuration().set_string("taskmanager.memory.task.off-heap.size", '80m')
+
# register the Python function
table_env.register_function("hash_code", udf(HashCode(), DataTypes.BIGINT(), DataTypes.BIGINT()))
@@ -58,6 +61,10 @@ my_table.select("string, bigint, bigint.hash_code(), hash_code(bigint)")
table_env.sql_query("SELECT string, bigint, hash_code(bigint) FROM MyTable")
{% endhighlight %}
+Note If not using RocksDB as state backend, you can also configure the python
+worker to use the managed memory of taskmanager by setting **python.fn-execution.memory.managed** to be **true**.
+Then there is no need to set the the configuration **taskmanager.memory.task.off-heap.size**.
+
It also supports to use Java/Scala scalar functions in Python Table API programs.
{% highlight python %}
@@ -76,6 +83,9 @@ public class HashCode extends ScalarFunction {
table_env = BatchTableEnvironment.create(env)
+# configure the off-heap memory of current taskmanager to enable the python worker uses off-heap memory.
+table_env.get_config().get_configuration().set_string("taskmanager.memory.task.off-heap.size", '80m')
+
# register the Java function
table_env.register_java_function("hash_code", "my.java.function.HashCode")
@@ -86,6 +96,10 @@ my_table.select("string.hash_code(), hash_code(string)")
table_env.sql_query("SELECT string, bigint, hash_code(string) FROM MyTable")
{% endhighlight %}
+Note If not using RocksDB as state backend, you can also configure the python
+worker to use the managed memory of taskmanager by setting **python.fn-execution.memory.managed** to be **true**.
+Then there is no need to set the the configuration **taskmanager.memory.task.off-heap.size**.
+
There are many ways to define a Python scalar function besides extending the base class `ScalarFunction`.
The following examples show the different ways to define a Python scalar function which takes two columns of
bigint as the input parameters and returns the sum of them as the result.
@@ -145,6 +159,9 @@ env = StreamExecutionEnvironment.get_execution_environment()
table_env = StreamTableEnvironment.create(env)
my_table = ... # type: Table, table schema: [a: String]
+# configure the off-heap memory of current taskmanager to enable the python worker uses off-heap memory.
+table_env.get_config().get_configuration().set_string("taskmanager.memory.task.off-heap.size", '80m')
+
# register the Python Table Function
table_env.register_function("split", udtf(Split(), DataTypes.STRING(), [DataTypes.STRING(), DataTypes.INT()]))
@@ -158,6 +175,9 @@ table_env.sql_query("SELECT a, word, length FROM MyTable LEFT JOIN LATERAL TABLE
{% endhighlight %}
+Note If not using RocksDB as state backend, you can also configure the python
+worker to use the managed memory of taskmanager by setting **python.fn-execution.memory.managed** to be **true**.
+Then there is no need to set the the configuration **taskmanager.memory.task.off-heap.size**.
It also supports to use Java/Scala table functions in Python Table API programs.
{% highlight python %}
@@ -182,6 +202,9 @@ env = StreamExecutionEnvironment.get_execution_environment()
table_env = StreamTableEnvironment.create(env)
my_table = ... # type: Table, table schema: [a: String]
+# configure the off-heap memory of current taskmanager to enable the python worker uses off-heap memory.
+table_env.get_config().get_configuration().set_string("taskmanager.memory.task.off-heap.size", '80m')
+
# Register the java function.
table_env.register_java_function("split", "my.java.function.Split")
@@ -198,6 +221,10 @@ table_env.sql_query("SELECT a, word, length FROM MyTable, LATERAL TABLE(split(a)
table_env.sql_query("SELECT a, word, length FROM MyTable LEFT JOIN LATERAL TABLE(split(a)) as T(word, length) ON TRUE")
{% endhighlight %}
+Note If not using RocksDB as state backend, you can also configure the python
+worker to use the managed memory of taskmanager by setting **python.fn-execution.memory.managed** to be **true**.
+Then there is no need to set the the configuration **taskmanager.memory.task.off-heap.size**.
+
Like Python scalar functions, you can use the above five ways to define Python TableFunctions.
Note The only difference is that the return type of Python Table Functions needs to be an iterable, iterator or generator.
diff --git a/docs/dev/table/python/python_udfs.zh.md b/docs/dev/table/python/python_udfs.zh.md
index b19120e01a37d7..1d83da55134ea1 100644
--- a/docs/dev/table/python/python_udfs.zh.md
+++ b/docs/dev/table/python/python_udfs.zh.md
@@ -48,6 +48,9 @@ class HashCode(ScalarFunction):
table_env = BatchTableEnvironment.create(env)
+# configure the off-heap memory of current taskmanager to enable the python worker uses off-heap memory.
+table_env.get_config().get_configuration().set_string("taskmanager.memory.task.off-heap.size", '80m')
+
# register the Python function
table_env.register_function("hash_code", udf(HashCode(), DataTypes.BIGINT(), DataTypes.BIGINT()))
@@ -58,6 +61,10 @@ my_table.select("string, bigint, bigint.hash_code(), hash_code(bigint)")
table_env.sql_query("SELECT string, bigint, hash_code(bigint) FROM MyTable")
{% endhighlight %}
+Note If not using RocksDB as state backend, you can also configure the python
+worker to use the managed memory of taskmanager by setting **python.fn-execution.memory.managed** to be **true**.
+Then there is no need to set the the configuration **taskmanager.memory.task.off-heap.size**.
+
It also supports to use Java/Scala scalar functions in Python Table API programs.
{% highlight python %}
@@ -76,6 +83,9 @@ public class HashCode extends ScalarFunction {
table_env = BatchTableEnvironment.create(env)
+# configure the off-heap memory of current taskmanager to enable the python worker uses off-heap memory.
+table_env.get_config().get_configuration().set_string("taskmanager.memory.task.off-heap.size", '80m')
+
# register the Java function
table_env.register_java_function("hash_code", "my.java.function.HashCode")
@@ -86,6 +96,10 @@ my_table.select("string.hash_code(), hash_code(string)")
table_env.sql_query("SELECT string, bigint, hash_code(string) FROM MyTable")
{% endhighlight %}
+Note If not using RocksDB as state backend, you can also configure the python
+worker to use the managed memory of taskmanager by setting **python.fn-execution.memory.managed** to be **true**.
+Then there is no need to set the the configuration **taskmanager.memory.task.off-heap.size**.
+
There are many ways to define a Python scalar function besides extending the base class `ScalarFunction`.
The following examples show the different ways to define a Python scalar function which takes two columns of
bigint as the input parameters and returns the sum of them as the result.
@@ -145,6 +159,9 @@ env = StreamExecutionEnvironment.get_execution_environment()
table_env = StreamTableEnvironment.create(env)
my_table = ... # type: Table, table schema: [a: String]
+# configure the off-heap memory of current taskmanager to enable the python worker uses off-heap memory.
+table_env.get_config().get_configuration().set_string("taskmanager.memory.task.off-heap.size", '80m')
+
# register the Python Table Function
table_env.register_function("split", udtf(Split(), DataTypes.STRING(), [DataTypes.STRING(), DataTypes.INT()]))
@@ -158,6 +175,9 @@ table_env.sql_query("SELECT a, word, length FROM MyTable LEFT JOIN LATERAL TABLE
{% endhighlight %}
+Note If not using RocksDB as state backend, you can also configure the python
+worker to use the managed memory of taskmanager by setting **python.fn-execution.memory.managed** to be **true**.
+Then there is no need to set the the configuration **taskmanager.memory.task.off-heap.size**.
It also supports to use Java/Scala table functions in Python Table API programs.
{% highlight python %}
@@ -182,6 +202,9 @@ env = StreamExecutionEnvironment.get_execution_environment()
table_env = StreamTableEnvironment.create(env)
my_table = ... # type: Table, table schema: [a: String]
+# configure the off-heap memory of current taskmanager to enable the python worker uses off-heap memory.
+table_env.get_config().get_configuration().set_string("taskmanager.memory.task.off-heap.size", '80m')
+
# Register the java function.
table_env.register_java_function("split", "my.java.function.Split")
@@ -198,6 +221,10 @@ table_env.sql_query("SELECT a, word, length FROM MyTable, LATERAL TABLE(split(a)
table_env.sql_query("SELECT a, word, length FROM MyTable LEFT JOIN LATERAL TABLE(split(a)) as T(word, length) ON TRUE")
{% endhighlight %}
+Note If not using RocksDB as state backend, you can also configure the python
+worker to use the managed memory of taskmanager by setting **python.fn-execution.memory.managed** to be **true**.
+Then there is no need to set the the configuration **taskmanager.memory.task.off-heap.size**.
+
Like Python scalar functions, you can use the above five ways to define Python TableFunctions.
Note The only difference is that the return type of Python Table Functions needs to be an iterable, iterator or generator.
diff --git a/docs/dev/table/python/vectorized_python_udfs.md b/docs/dev/table/python/vectorized_python_udfs.md
index 8eec40bf497bc4..ee5f03f937c1ef 100644
--- a/docs/dev/table/python/vectorized_python_udfs.md
+++ b/docs/dev/table/python/vectorized_python_udfs.md
@@ -54,6 +54,9 @@ def add(i, j):
table_env = BatchTableEnvironment.create(env)
+# configure the off-heap memory of current taskmanager to enable the python worker uses off-heap memory.
+table_env.get_config().get_configuration().set_string("taskmanager.memory.task.off-heap.size", '80m')
+
# register the vectorized Python scalar function
table_env.register_function("add", add)
@@ -63,3 +66,7 @@ my_table.select("add(bigint, bigint)")
# use the vectorized Python scalar function in SQL API
table_env.sql_query("SELECT add(bigint, bigint) FROM MyTable")
{% endhighlight %}
+
+Note If not using RocksDB as state backend, you can also configure the python
+worker to use the managed memory of taskmanager by setting **python.fn-execution.memory.managed** to be **true**.
+Then there is no need to set the the configuration **taskmanager.memory.task.off-heap.size**.
diff --git a/docs/dev/table/python/vectorized_python_udfs.zh.md b/docs/dev/table/python/vectorized_python_udfs.zh.md
index c50bc5b0de1002..b2d2ed922f958f 100644
--- a/docs/dev/table/python/vectorized_python_udfs.zh.md
+++ b/docs/dev/table/python/vectorized_python_udfs.zh.md
@@ -54,6 +54,9 @@ def add(i, j):
table_env = BatchTableEnvironment.create(env)
+# configure the off-heap memory of current taskmanager to enable the python worker uses off-heap memory.
+table_env.get_config().get_configuration().set_string("taskmanager.memory.task.off-heap.size", '80m')
+
# register the vectorized Python scalar function
table_env.register_function("add", add)
@@ -63,3 +66,7 @@ my_table.select("add(bigint, bigint)")
# use the vectorized Python scalar function in SQL API
table_env.sql_query("SELECT add(bigint, bigint) FROM MyTable")
{% endhighlight %}
+
+Note If not using RocksDB as state backend, you can also configure the python
+worker to use the managed memory of taskmanager by setting **python.fn-execution.memory.managed** to be **true**.
+Then there is no need to set the the configuration **taskmanager.memory.task.off-heap.size**.
diff --git a/docs/dev/table/sourceSinks.md b/docs/dev/table/sourceSinks.md
index 7b3647f0673669..e747c6f42018e4 100644
--- a/docs/dev/table/sourceSinks.md
+++ b/docs/dev/table/sourceSinks.md
@@ -22,796 +22,725 @@ specific language governing permissions and limitations
under the License.
-->
-A `TableSource` provides access to data which is stored in external systems (database, key-value store, message queue) or files. After a [TableSource is registered in a TableEnvironment](common.html#register-a-tablesource) it can be accessed by [Table API](tableApi.html) or [SQL]({{ site.baseurl }}/dev/table/sql/queries.html) queries.
+_Dynamic tables_ are the core concept of Flink's Table & SQL API for processing both bounded and unbounded
+data in a unified fashion.
-A `TableSink` [emits a Table](common.html#emit-a-table) to an external storage system, such as a database, key-value store, message queue, or file system (in different encodings, e.g., CSV, Parquet, or ORC).
+Because dynamic tables are only a logical concept, Flink does not own the data itself. Instead, the content
+of a dynamic table is stored in external systems (such as databases, key-value stores, message queues) or files.
-A `TableFactory` allows for separating the declaration of a connection to an external system from the actual implementation. A table factory creates configured instances of table sources and sinks from normalized, string-based properties. The properties can be generated programmatically using a `Descriptor` or via YAML configuration files for the [SQL Client](sqlClient.html).
+_Dynamic sources_ and _dynamic sinks_ can be used to read and write data from and to an external system. In
+the documentation, sources and sinks are often summarized under the term _connector_.
-Have a look at the [common concepts and API](common.html) page for details how to [register a TableSource](common.html#register-a-tablesource) and how to [emit a Table through a TableSink](common.html#emit-a-table). See the [built-in sources, sinks, and formats](connect.html) page for examples how to use factories.
+Flink provides pre-defined connectors for Kafka, Hive, and different file systems. See the [connector section]({% link dev/table/connectors/index.md %})
+for more information about built-in table sources and sinks.
-* This will be replaced by the TOC
-{:toc}
+This page focuses on how to develop a custom, user-defined connector.
-Define a TableSource
---------------------
+Attention New table source and table sink interfaces have been
+introduced in Flink 1.11 as part of [FLIP-95](https://cwiki.apache.org/confluence/display/FLINK/FLIP-95%3A+New+TableSource+and+TableSink+interfaces).
+Also the factory interfaces have been reworked. FLIP-95 is not fully implemented yet. Many ability interfaces
+are not supported yet (e.g. for filter or partition push down). If necessary, please also have a look
+at the [old table sources and sinks page]({% link dev/table/legacySourceSinks.md %}). Those interfaces
+are still supported for backwards compatibility.
-A `TableSource` is a generic interface that gives Table API and SQL queries access to data stored in an external system. It provides the schema of the table and the records that are mapped to rows with the table's schema. Depending on whether the `TableSource` is used in a streaming or batch query, the records are produced as a `DataSet` or `DataStream`.
+* This will be replaced by the TOC
+{:toc}
-If a `TableSource` is used in a streaming query it must implement the `StreamTableSource` interface, if it is used in a batch query it must implement the `BatchTableSource` interface. A `TableSource` can also implement both interfaces and be used in streaming and batch queries.
+Overview
+--------
-`StreamTableSource` and `BatchTableSource` extend the base interface `TableSource` that defines the following methods:
+In many cases, implementers don't need to create a new connector from scratch but would like to slightly
+modify existing connectors or hook into the existing stack. In other cases, implementers would like to
+create specialized connectors.
-
-
-{% highlight java %}
-TableSource {
+This section helps for both kinds of use cases. It explains the general architecture of table connectors
+from pure declaration in the API to runtime code that will be executed on the cluster.
- public TableSchema getTableSchema();
+The filled arrows show how objects are transformed to other objects from one stage to the next stage during
+the translation process.
- public TypeInformation getReturnType();
-
- public String explainSource();
-}
-{% endhighlight %}
+
+
-
-{% highlight scala %}
-TableSource[T] {
-
- def getTableSchema: TableSchema
+### Metadata
- def getReturnType: TypeInformation[T]
+Both Table API and SQL are declarative APIs. This includes the declaration of tables. Thus, executing
+a `CREATE TABLE` statement results in updated metadata in the target catalog.
- def explainSource: String
+For most catalog implementations, physical data in the external system is not modified for such an
+operation. Connector-specific dependencies don't have to be present in the classpath yet. The options declared
+in the `WITH` clause are neither validated nor otherwise interpreted.
-}
-{% endhighlight %}
-
-
-
-* `getTableSchema()`: Returns the schema of the produced table, i.e., the names and types of the fields of the table. The field types are defined using Flink's `DataType` (see [Table API types]({{ site.baseurl }}/dev/table/types.html) and [SQL types]({{ site.baseurl }}/dev/table/sql/index.html#data-types)). Note that the returned `TableSchema` shouldn't contain computed columns to reflect the schema of the physical `TableSource`.
+The metadata for dynamic tables (created via DDL or provided by the catalog) is represented as instances
+of `CatalogTable`. A table name will be resolved into a `CatalogTable` internally when necessary.
-* `getReturnType()`: Returns the physical type of the `DataStream` (`StreamTableSource`) or `DataSet` (`BatchTableSource`) and the records that are produced by the `TableSource`.
+### Planning
-* `explainSource()`: Returns a String that describes the `TableSource`. This method is optional and used for display purposes only.
+When it comes to planning and optimization of the table program, a `CatalogTable` needs to be resolved
+into a `DynamicTableSource` (for reading in a `SELECT` query) and `DynamicTableSink` (for writing in
+an `INSERT INTO` statement).
-The `TableSource` interface separates the logical table schema from the physical type of the returned `DataStream` or `DataSet`. As a consequence, all fields of the table schema (`getTableSchema()`) must be mapped to a field with corresponding type of the physical return type (`getReturnType()`). By default, this mapping is done based on field names. For example, a `TableSource` that defines a table schema with two fields `[name: String, size: Integer]` requires a `TypeInformation` with at least two fields called `name` and `size` of type `String` and `Integer`, respectively. This could be a `PojoTypeInfo` or a `RowTypeInfo` that have two fields named `name` and `size` with matching types.
+`DynamicTableSourceFactory` and `DynamicTableSinkFactory` provide connector-specific logic for translating
+the metadata of a `CatalogTable` into instances of `DynamicTableSource` and `DynamicTableSink`. In most
+of the cases, a factory's purpose is to validate options (such as `'port' = '5022'` in the example),
+configure encoding/decoding formats (if required), and create a parameterized instance of the table
+connector.
-However, some types, such as Tuple or CaseClass types, do support custom field names. If a `TableSource` returns a `DataStream` or `DataSet` of a type with fixed field names, it can implement the `DefinedFieldMapping` interface to map field names from the table schema to field names of the physical return type.
+By default, instances of `DynamicTableSourceFactory` and `DynamicTableSinkFactory` are discovered using
+Java's [Service Provider Interfaces (SPI)](https://docs.oracle.com/javase/tutorial/sound/SPI-intro.html). The
+`connector` option (such as `'connector' = 'custom'` in the example) must correspond to a valid factory
+identifier.
-### Defining a BatchTableSource
+Although it might not be apparent in the class naming, `DynamicTableSource` and `DynamicTableSink`
+can also be seen as stateful factories that eventually produce concrete runtime implementation for reading/writing
+the actual data.
-The `BatchTableSource` interface extends the `TableSource` interface and defines one additional method:
+The planner uses the source and sink instances to perform connector-specific bidirectional comunication
+until an optimal logical plan could be found. Depending on the optionally declared ability interfaces (e.g.
+`SupportsProjectionPushDown` or `SupportsOverwrite`), the planner might apply changes to an instance and
+thus mutate the produced runtime implementation.
-
-{% highlight scala %}
-BatchTableSource[T] extends TableSource[T] {
+Once the logical planning is complete, the planner will obtain the _runtime implementation_ from the table
+connector. Runtime logic is implemented in Flink's core connector interfaces such as `InputFormat` or `SourceFunction`.
- def getDataSet(execEnv: ExecutionEnvironment): DataSet[T]
-}
-{% endhighlight %}
-
-
+Those interfaces are grouped by another level of abstraction as subclasses of `ScanRuntimeProvider`,
+`LookupRuntimeProvider`, and `SinkRuntimeProvider`.
-* `getDataSet(execEnv)`: Returns a `DataSet` with the data of the table. The type of the `DataSet` must be identical to the return type defined by the `TableSource.getReturnType()` method. The `DataSet` can by created using a regular [data source]({{ site.baseurl }}/dev/batch/#data-sources) of the DataSet API. Commonly, a `BatchTableSource` is implemented by wrapping a `InputFormat` or [batch connector]({{ site.baseurl }}/dev/batch/connectors.html).
+For example, both `OutputFormatProvider` (providing `org.apache.flink.api.common.io.OutputFormat`) and `SinkFunctionProvider` (providing `org.apache.flink.streaming.api.functions.sink.SinkFunction`) are concrete instances of `SinkRuntimeProvider`
+that the planner can handle.
{% top %}
-### Defining a StreamTableSource
+Extension Points
+----------------
-The `StreamTableSource` interface extends the `TableSource` interface and defines one additional method:
+This section explains the available interfaces for extending Flink's table connectors.
-
+Dynamic table factories are used to configure a dynamic table connector for an external storage system from catalog
+and session information.
-
-{% highlight scala %}
-StreamTableSource[T] extends TableSource[T] {
+`org.apache.flink.table.factories.DynamicTableSourceFactory` can be implemented to construct a `DynamicTableSource`.
- def getDataStream(execEnv: StreamExecutionEnvironment): DataStream[T]
-}
-{% endhighlight %}
-
-
+`org.apache.flink.table.factories.DynamicTableSinkFactory` can be implemented to construct a `DynamicTableSink`.
-* `getDataStream(execEnv)`: Returns a `DataStream` with the data of the table. The type of the `DataStream` must be identical to the return type defined by the `TableSource.getReturnType()` method. The `DataStream` can by created using a regular [data source]({{ site.baseurl }}/dev/datastream_api.html#data-sources) of the DataStream API. Commonly, a `StreamTableSource` is implemented by wrapping a `SourceFunction` or a [stream connector]({{ site.baseurl }}/dev/connectors/).
+By default, the factory is discovered using the value of the `connector` option as the factory identifier
+and Java's Service Provider Interface.
-{% top %}
+In JAR files, references to new implementations can be added to the service file:
-### Defining a TableSource with Time Attributes
+`META-INF/services/org.apache.flink.table.factories.Factory`
-Time-based operations of streaming [Table API](tableApi.html#group-windows) and [SQL]({{ site.baseurl }}/dev/table/sql/queries.html#group-windows) queries, such as windowed aggregations or joins, require explicitly specified [time attributes](streaming/time_attributes.html).
+The framework will check for a single matching factory that is uniquely identified by factory identifier
+and requested base class (e.g. `DynamicTableSourceFactory`).
-A `TableSource` defines a time attribute as a field of type `Types.SQL_TIMESTAMP` in its table schema. In contrast to all regular fields in the schema, a time attribute must not be matched to a physical field in the return type of the table source. Instead, a `TableSource` defines a time attribute by implementing a certain interface.
+The factory discovery process can be bypassed by the catalog implementation if necessary. For this, a
+catalog needs to return an instance that implements the requested base class in `org.apache.flink.table.catalog.Catalog#getFactory`.
-#### Defining a Processing Time Attribute
+### Dynamic Table Source
-[Processing time attributes](streaming/time_attributes.html#processing-time) are commonly used in streaming queries. A processing time attribute returns the current wall-clock time of the operator that accesses it. A `TableSource` defines a processing time attribute by implementing the `DefinedProctimeAttribute` interface. The interface looks as follows:
+By definition, a dynamic table can change over time.
-
-
-{% highlight java %}
-DefinedProctimeAttribute {
+When reading a dynamic table, the content can either be considered as:
+- A changelog (finite or infinite) for which all changes are consumed continuously until the changelog
+ is exhausted. This is represented by the `ScanTableSource` interface.
+- A continuously changing or very large external table whose content is usually never read entirely
+ but queried for individual values when necessary. This is represented by the `LookupTableSource`
+ interface.
- public String getProctimeAttribute();
-}
-{% endhighlight %}
-
+A class can implement both of these interfaces at the same time. The planner decides about their usage depending
+on the specified query.
-
+A `ScanTableSource` scans all rows from an external storage system during runtime.
-* `getProctimeAttribute()`: Returns the name of the processing time attribute. The specified attribute must be defined of type `Types.SQL_TIMESTAMP` in the table schema and can be used in time-based operations. A `DefinedProctimeAttribute` table source can define no processing time attribute by returning `null`.
+The scanned rows don't have to contain only insertions but can also contain updates and deletions. Thus,
+the table source can be used to read a (finite or infinite) changelog. The returned _changelog mode_ indicates
+the set of changes that the planner can expect during runtime.
-Attention Both `StreamTableSource` and `BatchTableSource` can implement `DefinedProctimeAttribute` and define a processing time attribute. In case of a `BatchTableSource` the processing time field is initialized with the current timestamp during the table scan.
+For regular batch scenarios, the source can emit a bounded stream of insert-only rows.
-#### Defining a Rowtime Attribute
+For regular streaming scenarios, the source can emit an unbounded stream of insert-only rows.
-[Rowtime attributes](streaming/time_attributes.html#event-time) are attributes of type `TIMESTAMP` and handled in a unified way in stream and batch queries.
+For change data capture (CDC) scenarios, the source can emit bounded or unbounded streams with insert,
+update, and delete rows.
-A table schema field of type `SQL_TIMESTAMP` can be declared as rowtime attribute by specifying
+A table source can implement further abilitiy interfaces such as `SupportsProjectionPushDown` that might
+mutate an instance during planning. All abilities are listed in the `org.apache.flink.table.connector.source.abilities`
+package and in the documentation of `org.apache.flink.table.connector.source.ScanTableSource`.
-* the name of the field,
-* a `TimestampExtractor` that computes the actual value for the attribute (usually from one or more other fields), and
-* a `WatermarkStrategy` that specifies how watermarks are generated for the the rowtime attribute.
+The runtime implementation of a `ScanTableSource` must produce internal data structures. Thus, records
+must be emitted as `org.apache.flink.table.data.RowData`. The framework provides runtime converters such
+that a source can still work on common data structures and perform a conversion at the end.
-A `TableSource` defines a rowtime attribute by implementing the `DefinedRowtimeAttributes` interface. The interface looks as follows:
+#### Lookup Table Source
-
-
-{% highlight java %}
-DefinedRowtimeAttribute {
-
- public List getRowtimeAttributeDescriptors();
-}
-{% endhighlight %}
-
+A `LookupTableSource` looks up rows of an external storage system by one or more keys during runtime.
-
-{% highlight scala %}
-DefinedRowtimeAttributes {
+Compared to `ScanTableSource`, the source does not have to read the entire table and can lazily fetch individual
+values from a (possibly continuously changing) external table when necessary.
- def getRowtimeAttributeDescriptors: util.List[RowtimeAttributeDescriptor]
-}
-{% endhighlight %}
-
-
+Compared to `ScanTableSource`, a `LookupTableSource` does only support emitting insert-only changes currently.
-* `getRowtimeAttributeDescriptors()`: Returns a list of `RowtimeAttributeDescriptor`. A `RowtimeAttributeDescriptor` describes a rowtime attribute with the following properties:
- * `attributeName`: The name of the rowtime attribute in the table schema. The field must be defined with type `Types.SQL_TIMESTAMP`.
- * `timestampExtractor`: The timestamp extractor extracts the timestamp from a record with the return type. For example, it can convert a Long field into a timestamp or parse a String-encoded timestamp. Flink comes with a set of built-in `TimestampExtractor` implementation for common use cases. It is also possible to provide a custom implementation.
- * `watermarkStrategy`: The watermark strategy defines how watermarks are generated for the rowtime attribute. Flink comes with a set of built-in `WatermarkStrategy` implementations for common use cases. It is also possible to provide a custom implementation.
+Further abilities are not supported. See the documentation of `org.apache.flink.table.connector.source.LookupTableSource`
+for more information.
-Attention Although the `getRowtimeAttributeDescriptors()` method returns a list of descriptors, only a single rowtime attribute is support at the moment. We plan to remove this restriction in the future and support tables with more than one rowtime attribute.
+The runtime implementation of a `LookupTableSource` is a `TableFunction` or `AsyncTableFunction`. The function
+will be called with values for the given lookup keys during runtime.
-Attention Both, `StreamTableSource` and `BatchTableSource`, can implement `DefinedRowtimeAttributes` and define a rowtime attribute. In either case, the rowtime field is extracted using the `TimestampExtractor`. Hence, a `TableSource` that implements `StreamTableSource` and `BatchTableSource` and defines a rowtime attribute provides exactly the same data to streaming and batch queries.
+### Dynamic Table Sink
-##### Provided Timestamp Extractors
+By definition, a dynamic table can change over time.
-Flink provides `TimestampExtractor` implementations for common use cases.
+When writing a dynamic table, the content can always be considered as a changelog (finite or infinite)
+for which all changes are written out continuously until the changelog is exhausted. The returned _changelog mode_
+indicates the set of changes that the sink accepts during runtime.
-The following `TimestampExtractor` implementations are currently available:
+For regular batch scenarios, the sink can solely accept insert-only rows and write out bounded streams.
-* `ExistingField(fieldName)`: Extracts the value of a rowtime attribute from an existing `LONG`, `SQL_TIMESTAMP`, or timestamp formatted `STRING` field. One example of such a string would be '2018-05-28 12:34:56.000'.
-* `StreamRecordTimestamp()`: Extracts the value of a rowtime attribute from the timestamp of the `DataStream` `StreamRecord`. Note, this `TimestampExtractor` is not available for batch table sources.
+For regular streaming scenarios, the sink can solely accept insert-only rows and can write out unbounded streams.
-A custom `TimestampExtractor` can be defined by implementing the corresponding interface.
+For change data capture (CDC) scenarios, the sink can write out bounded or unbounded streams with insert,
+update, and delete rows.
-##### Provided Watermark Strategies
+A table sink can implement further abilitiy interfaces such as `SupportsOverwrite` that might mutate an
+instance during planning. All abilities are listed in the `org.apache.flink.table.connector.sink.abilities`
+package and in the documentation of `org.apache.flink.table.connector.sink.DynamicTableSink`.
-Flink provides `WatermarkStrategy` implementations for common use cases.
+The runtime implementation of a `DynamicTableSink` must consume internal data structures. Thus, records
+must be accepted as `org.apache.flink.table.data.RowData`. The framework provides runtime converters such
+that a sink can still work on common data structures and perform a conversion at the beginning.
-The following `WatermarkStrategy` implementations are currently available:
+### Encoding / Decoding Formats
-* `AscendingTimestamps`: A watermark strategy for ascending timestamps. Records with timestamps that are out-of-order will be considered late.
-* `BoundedOutOfOrderTimestamps(delay)`: A watermark strategy for timestamps that are at most out-of-order by the specified delay.
-* `PreserveWatermarks()`: A strategy which indicates the watermarks should be preserved from the underlying `DataStream`.
+Some table connectors accept different formats that encode and decode keys and/or values.
-A custom `WatermarkStrategy` can be defined by implementing the corresponding interface.
+Formats work similar to the pattern `DynamicTableSourceFactory -> DynamicTableSource -> ScanRuntimeProvider`,
+where the factory is responsible for translating options and the source is responsible for creating runtime logic.
-{% top %}
+Because formats might be located in different modules, they are discovered using Java's Service Provider
+Interface similar to [table factories](#dynamic-table-factories). In order to discover a format factory,
+the dynamic table factory searches for a factory that corresponds to a factory identifier and connector-specific
+base class.
-### Defining a TableSource with Projection Push-Down
+For example, the Kafka table source requires a `DeserializationSchema` as runtime interface for a decoding
+format. Therefore, the Kafka table source factory uses the value of the `value.format` option to discover
+a `DeserializationFormatFactory`.
-A `TableSource` supports projection push-down by implementing the `ProjectableTableSource` interface. The interface defines a single method:
+The following format factories are currently supported:
-
+The format factory translates the options into an `EncodingFormat` or a `DecodingFormat`. Those interfaces are
+another kind of factory that produce specialized format runtime logic for the given data type.
-
-
-* `projectFields(fields)`: Returns a *copy* of the `TableSource` with adjusted physical return type. The `fields` parameter provides the indexes of the fields that must be provided by the `TableSource`. The indexes relate to the `TypeInformation` of the physical return type, *not* to the logical table schema. The copied `TableSource` must adjust its return type and the returned `DataStream` or `DataSet`. The `TableSchema` of the copied `TableSource` must not be changed, i.e, it must be the same as the original `TableSource`. If the `TableSource` implements the `DefinedFieldMapping` interface, the field mapping must be adjusted to the new return type.
-
-Attention In order for Flink to distinguish a projection push-down table source from its original form, `explainSource` method must be override to include information regarding the projected fields.
-
-The `ProjectableTableSource` adds support to project flat fields. If the `TableSource` defines a table with nested schema, it can implement the `NestedFieldsProjectableTableSource` to extend the projection to nested fields. The `NestedFieldsProjectableTableSource` is defined as follows:
-
-
-
-* `projectNestedField(fields, nestedFields)`: Returns a *copy* of the `TableSource` with adjusted physical return type. Fields of the physical return type may be removed or reordered but their type must not be changed. The contract of this method is essentially the same as for the `ProjectableTableSource.projectFields()` method. In addition, the `nestedFields` parameter contains for each field index in the `fields` list, a list of paths to all nested fields that are accessed by the query. All other nested fields do not need to be read, parsed, and set in the records that are produced by the `TableSource`.
-
-Attention the types of the projected fields must not be changed but unused fields may be set to null or to a default value.
-
-{% top %}
-
-### Defining a TableSource with Filter Push-Down
-
-The `FilterableTableSource` interface adds support for filter push-down to a `TableSource`. A `TableSource` extending this interface is able to filter records such that the returned `DataStream` or `DataSet` returns fewer records.
-
-The interface looks as follows:
-
-
-
-* `applyPredicate(predicates)`: Returns a *copy* of the `TableSource` with added predicates. The `predicates` parameter is a mutable list of conjunctive predicates that are "offered" to the `TableSource`. The `TableSource` accepts to evaluate a predicate by removing it from the list. Predicates that are left in the list will be evaluated by a subsequent filter operator.
-* `isFilterPushedDown()`: Returns true if the `applyPredicate()` method was called before. Hence, `isFilterPushedDown()` must return true for all `TableSource` instances returned from a `applyPredicate()` call.
-
-Attention In order for Flink to distinguish a filter push-down table source from its original form, `explainSource` method must be override to include information regarding the push-down filters.
-
-{% top %}
-
-### Defining a TableSource for Lookups
-
-Attention This is an experimental feature. The interface may be changed in future versions. It's only supported in Blink planner.
-
-The `LookupableTableSource` interface adds support for the table to be accessed via key column(s) in a lookup fashion. This is very useful when used to join with a dimension table to enrich some information. If you want to use the `TableSource` in lookup mode, you should use the source in [temporal table join syntax](streaming/joins.html).
-
-The interface looks as follows:
-
-
-
-{% highlight java %}
-LookupableTableSource implements TableSource {
-
- public TableFunction getLookupFunction(String[] lookupkeys);
-
- public AsyncTableFunction getAsyncLookupFunction(String[] lookupkeys);
-
- public boolean isAsyncEnabled();
-}
-{% endhighlight %}
-
-
-* `getLookupFunction(lookupkeys)`: Returns a `TableFunction` which used to lookup the matched row(s) via lookup keys. The lookupkeys are the field names of `LookupableTableSource` in the join equal conditions. The eval method parameters of the returned `TableFunction`'s should be in the order which `lookupkeys` defined. It is recommended to define the parameters in varargs (e.g. `eval(Object... lookupkeys)` to match all the cases). The return type of the `TableFunction` must be identical to the return type defined by the `TableSource.getReturnType()` method.
-* `getAsyncLookupFunction(lookupkeys)`: Optional. Similar to `getLookupFunction`, but the `AsyncLookupFunction` lookups the matched row(s) asynchronously. The underlying of `AsyncLookupFunction` will be called via [Async I/O]({{ site.baseurl }}/dev/stream/operators/asyncio.html). The first argument of the eval method of the returned `AsyncTableFunction` should be defined as `java.util.concurrent.CompletableFuture` to collect results asynchronously (e.g. `eval(CompletableFuture> result, Object... lookupkeys)`). The implementation of this method can throw an exception if the TableSource doesn't support asynchronously lookup.
-* `isAsyncEnabled()`: Returns true if async lookup is enabled. It requires `getAsyncLookupFunction(lookupkeys)` is implemented if `isAsyncEnabled` returns true.
+For example, for a Kafka table source factory, the `DeserializationFormatFactory` would return an `EncodingFormat`
+that can be passed into the Kafka table source.
{% top %}
-Define a TableSink
+Full Stack Example
------------------
-A `TableSink` specifies how to emit a `Table` to an external system or location. The interface is generic such that it can support different storage locations and formats. There are different table sinks for batch tables and streaming tables.
+This section sketches how to implement a scan table source with a decoding format that supports changelog
+semantics. The example illustrates how all of the mentioned components play together. It can serve as
+a reference implementation.
-The general interface looks as follows:
+In particular, it shows how to
+- create factories that parse and validate options,
+- implement table connectors,
+- implement and discover custom formats,
+- and use provided utilities such as data structure converters and the `FactoryUtil`.
-
-
-{% highlight java %}
-TableSink {
-
- public TypeInformation getOutputType();
+The table source uses a simple single-threaded `SourceFunction` to open a socket that listens for incoming
+bytes. The raw bytes are decoded into rows by a pluggable format. The format expects a changelog flag
+as the first column.
- public String[] getFieldNames();
+We will use most of the interfaces metioned above to enable the following DDL:
- public TypeInformation[] getFieldTypes();
-
- public TableSink configure(String[] fieldNames, TypeInformation[] fieldTypes);
-}
+{% highlight sql %}
+CREATE TABLE UserScores (name STRING, score INT)
+WITH (
+ 'connector' = 'socket',
+ 'hostname' = 'localhost',
+ 'port' = '9999',
+ 'byte-delimiter' = '10',
+ 'format' = 'changelog-csv',
+ 'changelog-csv.column-delimiter' = '|'
+);
{% endhighlight %}
-
-
-
-{% highlight scala %}
-TableSink[T] {
-
- def getOutputType: TypeInformation
- def getFieldNames: Array[String]
+Because the format supports changelog semantics, we are able to ingest updates during runtime and create
+an updating view that can continuously evaluate changing data:
- def getFieldTypes: Array[TypeInformation]
-
- def configure(fieldNames: Array[String], fieldTypes: Array[TypeInformation]): TableSink[T]
-}
+{% highlight sql %}
+SELECT name, SUM(score) FROM UserScores GROUP BY name;
{% endhighlight %}
-
-
-
-The `TableSink#configure` method is called to pass the schema of the Table (field names and types) to emit to the `TableSink`. The method must return a new instance of the TableSink which is configured to emit the provided Table schema. Note that the provided `TableSchema` shouldn't contain computed columns to reflect the schema of the physical `TableSink`.
-
-### BatchTableSink
-Defines an external `TableSink` to emit a batch table.
-
-The interface looks as follows:
-
-
-
-{% highlight java %}
-BatchTableSink implements TableSink {
-
- public void emitDataSet(DataSet dataSet);
-}
+Use the following command to ingest data in a terminal:
+{% highlight text %}
+> nc -lk 9999
+INSERT|Alice|12
+INSERT|Bob|5
+DELETE|Alice|12
+INSERT|Alice|18
{% endhighlight %}
-
-
-{% top %}
+This section illustrates how to translate metadata coming from the catalog to concrete connector instances.
-### AppendStreamTableSink
+Both factories have been added to the `META-INF/services` directory.
-Defines an external `TableSink` to emit a streaming table with only insert changes.
+**`SocketDynamicTableFactory`**
-The interface looks as follows:
+The `SocketDynamicTableFactory` translates the catalog table to a table source. Because the table source
+requires a decoding format, we are discovering the format using the provided `FactoryUtil` for convenience.
-
-
{% highlight java %}
-AppendStreamTableSink implements TableSink {
+import org.apache.flink.api.common.serialization.DeserializationSchema;
+import org.apache.flink.configuration.ConfigOption;
+import org.apache.flink.configuration.ConfigOptions;
+import org.apache.flink.configuration.ReadableConfig;
+import org.apache.flink.table.connector.format.DecodingFormat;
+import org.apache.flink.table.connector.source.DynamicTableSource;
+import org.apache.flink.table.data.RowData;
+import org.apache.flink.table.factories.DeserializationFormatFactory;
+import org.apache.flink.table.factories.DynamicTableSourceFactory;
+import org.apache.flink.table.factories.FactoryUtil;
+import org.apache.flink.table.types.DataType;
+
+public class SocketDynamicTableFactory implements DynamicTableSourceFactory {
+
+ // define all options statically
+ public static final ConfigOption HOSTNAME = ConfigOptions.key("hostname")
+ .stringType()
+ .noDefaultValue();
+
+ public static final ConfigOption PORT = ConfigOptions.key("port")
+ .intType()
+ .noDefaultValue();
+
+ public static final ConfigOption BYTE_DELIMITER = ConfigOptions.key("byte-delimiter")
+ .intType()
+ .defaultValue(10); // corresponds to '\n'
- public DataStreamSink> consumeDataStream(DataStream dataStream);
-}
-{% endhighlight %}
-
+ @Override
+ public String factoryIdentifier() {
+ return "socket"; // used for matching to `connector = '...'`
+ }
-
-{% highlight scala %}
-AppendStreamTableSink[T] extends TableSink[T] {
+ @Override
+ public Set> requiredOptions() {
+ final Set> options = new HashSet<>();
+ options.add(HOSTNAME);
+ options.add(PORT);
+ options.add(FactoryUtil.FORMAT); // use pre-defined option for format
+ return options;
+ }
- def consumeDataStream(dataStream: DataStream[T]): DataStreamSink[_]
+ @Override
+ public Set> optionalOptions() {
+ final Set> options = new HashSet<>();
+ options.add(BYTE_DELIMITER);
+ return options;
+ }
+
+ @Override
+ public DynamicTableSource createDynamicTableSource(Context context) {
+ // either implement your custom validation logic here ...
+ // or use the provided helper utility
+ final FactoryUtil.TableFactoryHelper helper = FactoryUtil.createTableFactoryHelper(this, context);
+
+ // discover a suitable decoding format
+ final DecodingFormat> decodingFormat = helper.discoverDecodingFormat(
+ DeserializationFormatFactory.class,
+ FactoryUtil.FORMAT);
+
+ // validate all options
+ helper.validate();
+
+ // get the validated options
+ final ReadableConfig options = helper.getOptions();
+ final String hostname = options.get(HOSTNAME);
+ final int port = options.get(PORT);
+ final byte byteDelimiter = (byte) (int) options.get(BYTE_DELIMITER);
+
+ // derive the produced data type (excluding computed columns) from the catalog table
+ final DataType producedDataType = context.getCatalogTable().getSchema().toPhysicalRowDataType();
+
+ // create and return dynamic table source
+ return new SocketDynamicTableSource(hostname, port, byteDelimiter, decodingFormat, producedDataType);
+ }
}
{% endhighlight %}
-
-
-
-If the table is also modified by update or delete changes, a `TableException` will be thrown.
-{% top %}
+**`ChangelogCsvFormatFactory`**
-### RetractStreamTableSink
+The `ChangelogCsvFormatFactory` translates format-specific options to a format. The `FactoryUtil` in `SocketDynamicTableFactory`
+takes care of adapting the option keys accordingly and handles the prefixing like `changelog-csv.column-delimiter`.
-Defines an external `TableSink` to emit a streaming table with insert, update, and delete changes.
+Because this factory implements `DeserializationFormatFactory`, it could also be used for other connectors
+that support deserialization formats such as the Kafka connector.
-The interface looks as follows:
-
-
-{% highlight scala %}
-RetractStreamTableSink[T] extends TableSink[Tuple2[Boolean, T]] {
+ @Override
+ public Set> requiredOptions() {
+ return Collections.emptySet();
+ }
- def getRecordType: TypeInformation[T]
+ @Override
+ public Set> optionalOptions() {
+ final Set> options = new HashSet<>();
+ options.add(COLUMN_DELIMITER);
+ return options;
+ }
- def consumeDataStream(dataStream: DataStream[Tuple2[Boolean, T]]): DataStreamSink[_]
+ @Override
+ public DecodingFormat> createDecodingFormat(
+ DynamicTableFactory.Context context,
+ ReadableConfig formatOptions) {
+ // either implement your custom validation logic here ...
+ // or use the provided helper method
+ FactoryUtil.validateFactoryOptions(this, formatOptions);
+
+ // get the validated options
+ final String columnDelimiter = formatOptions.get(COLUMN_DELIMITER);
+
+ // create and return the format
+ return new ChangelogCsvFormat(columnDelimiter);
+ }
}
{% endhighlight %}
-
-
-
-The table will be converted into a stream of accumulate and retraction messages which are encoded as Java `Tuple2`. The first field is a boolean flag to indicate the message type (`true` indicates insert, `false` indicates delete). The second field holds the record of the requested type `T`.
-{% top %}
+### Table Source and Decoding Format
-### UpsertStreamTableSink
+This section illustrates how to translate from instances of the planning layer to runtime instances that
+are shipped to the cluster.
-Defines an external `TableSink` to emit a streaming table with insert, update, and delete changes.
+**`SocketDynamicTableSource`**
-The interface looks as follows:
+The `SocketDynamicTableSource` is used during planning. In our example, we don't implement any of the
+available ability interfaces. Therefore, the main logic can be found in `getScanRuntimeProvider(...)`
+where we instantiate the required `SourceFunction` and its `DeserializationSchema` for runtime. Both
+instances are parameterized to return internal data structures (i.e. `RowData`).
-
-
{% highlight java %}
-UpsertStreamTableSink implements TableSink> {
-
- public void setKeyFields(String[] keys);
+import org.apache.flink.api.common.serialization.DeserializationSchema;
+import org.apache.flink.streaming.api.functions.source.SourceFunction;
+import org.apache.flink.table.connector.ChangelogMode;
+import org.apache.flink.table.connector.format.DecodingFormat;
+import org.apache.flink.table.connector.source.DynamicTableSource;
+import org.apache.flink.table.connector.source.ScanTableSource;
+import org.apache.flink.table.connector.source.SourceFunctionProvider;
+import org.apache.flink.table.data.RowData;
+import org.apache.flink.table.types.DataType;
+
+public class SocketDynamicTableSource implements ScanTableSource {
+
+ private final String hostname;
+ private final int port;
+ private final byte byteDelimiter;
+ private final DecodingFormat> decodingFormat;
+ private final DataType producedDataType;
+
+ public SocketDynamicTableSource(
+ String hostname,
+ int port,
+ byte byteDelimiter,
+ DecodingFormat> decodingFormat,
+ DataType producedDataType) {
+ this.hostname = hostname;
+ this.port = port;
+ this.byteDelimiter = byteDelimiter;
+ this.decodingFormat = decodingFormat;
+ this.producedDataType = producedDataType;
+ }
- public void setIsAppendOnly(boolean isAppendOnly);
+ @Override
+ public ChangelogMode getChangelogMode() {
+ // in our example the format decides about the changelog mode
+ // but it could also be the source itself
+ return decodingFormat.getChangelogMode();
+ }
- public TypeInformation getRecordType();
+ @Override
+ public ScanRuntimeProvider getScanRuntimeProvider(ScanContext runtimeProviderContext) {
- public DataStreamSink> consumeDataStream(DataStream> dataStream);
-}
-{% endhighlight %}
-
+ // create runtime classes that are shipped to the cluster
-
-The table must be have unique key fields (atomic or composite) or be append-only. If the table does not have a unique key and is not append-only, a `TableException` will be thrown. The unique key of the table is configured by the `UpsertStreamTableSink#setKeyFields()` method.
+**`ChangelogCsvFormat`**
-The table will be converted into a stream of upsert and delete messages which are encoded as a Java `Tuple2`. The first field is a boolean flag to indicate the message type. The second field holds the record of the requested type `T`.
+The `ChangelogCsvFormat` is a decoding format that uses a `DeserializationSchema` during runtime. It
+supports emitting `INSERT` and `DELETE` changes.
-A message with true boolean field is an upsert message for the configured key. A message with false flag is a delete message for the configured key. If the table is append-only, all messages will have a true flag and must be interpreted as insertions.
-
-{% top %}
-
-Define a TableFactory
----------------------
-
-A `TableFactory` allows to create different table-related instances from string-based properties. All available factories are called for matching to the given set of properties and a corresponding factory class.
-
-Factories leverage Java's [Service Provider Interfaces (SPI)](https://docs.oracle.com/javase/tutorial/sound/SPI-intro.html) for discovering. This means that every dependency and JAR file should contain a file `org.apache.flink.table.factories.TableFactory` in the `META_INF/services` resource directory that lists all available table factories that it provides.
-
-Every table factory needs to implement the following interface:
-
-
-{% highlight scala %}
-package org.apache.flink.table.factories
-
-trait TableFactory {
+import org.apache.flink.api.common.serialization.DeserializationSchema;
+import org.apache.flink.api.common.typeinfo.TypeInformation;
+import org.apache.flink.table.connector.ChangelogMode;
+import org.apache.flink.table.connector.format.DecodingFormat;
+import org.apache.flink.table.connector.source.DynamicTableSource;
+import org.apache.flink.table.connector.source.DynamicTableSource.DataStructureConverter;
+import org.apache.flink.table.data.RowData;
+import org.apache.flink.table.types.DataType;
+import org.apache.flink.table.types.logical.LogicalType;
+import org.apache.flink.types.RowKind;
+
+public class ChangelogCsvFormat implements DecodingFormat> {
+
+ private final String columnDelimiter;
+
+ public ChangelogCsvFormat(String columnDelimiter) {
+ this.columnDelimiter = columnDelimiter;
+ }
- def requiredContext(): util.Map[String, String]
+ @Override
+ @SuppressWarnings("unchecked")
+ public DeserializationSchema createRuntimeDecoder(
+ DynamicTableSource.Context context,
+ DataType producedDataType) {
+ // create type information for the DeserializationSchema
+ final TypeInformation producedTypeInfo = (TypeInformation) context.createTypeInformation(
+ producedDataType);
+
+ // most of the code in DeserializationSchema will not work on internal data structures
+ // create a converter for conversion at the end
+ final DataStructureConverter converter = context.createDataStructureConverter(producedDataType);
+
+ // use logical types during runtime for parsing
+ final List parsingTypes = producedDataType.getLogicalType().getChildren();
+
+ // create runtime class
+ return new ChangelogCsvDeserializer(parsingTypes, converter, producedTypeInfo, columnDelimiter);
+ }
- def supportedProperties(): util.List[String]
+ @Override
+ public ChangelogMode getChangelogMode() {
+ // define that this format can produce INSERT and DELETE rows
+ return ChangelogMode.newBuilder()
+ .addContainedKind(RowKind.INSERT)
+ .addContainedKind(RowKind.DELETE)
+ .build();
+ }
}
{% endhighlight %}
-
-
-* `requiredContext()`: Specifies the context that this factory has been implemented for. The framework guarantees to only match for this factory if the specified set of properties and values are met. Typical properties might be `connector.type`, `format.type`, or `update-mode`. Property keys such as `connector.property-version` and `format.property-version` are reserved for future backwards compatibility cases.
-* `supportedProperties()`: List of property keys that this factory can handle. This method will be used for validation. If a property is passed that this factory cannot handle, an exception will be thrown. The list must not contain the keys that are specified by the context.
+### Runtime
-In order to create a specific instance, a factory class can implement one or more interfaces provided in `org.apache.flink.table.factories`:
+For completeness, this section illustrates the runtime logic for both `SourceFunction` and `DeserializationSchema`.
-* `BatchTableSourceFactory`: Creates a batch table source.
-* `BatchTableSinkFactory`: Creates a batch table sink.
-* `StreamTableSourceFactory`: Creates a stream table source.
-* `StreamTableSinkFactory`: Creates a stream table sink.
-* `DeserializationSchemaFactory`: Creates a deserialization schema format.
-* `SerializationSchemaFactory`: Creates a serialization schema format.
+**ChangelogCsvDeserializer**
-The discovery of a factory happens in multiple stages:
+The `ChangelogCsvDeserializer` contains a simple parsing logic for converting bytes into `Row` of `Integer`
+and `String` with a row kind. The final conversion step converts those into internal data structures.
-- Discover all available factories.
-- Filter by factory class (e.g., `StreamTableSourceFactory`).
-- Filter by matching context.
-- Filter by supported properties.
-- Verify that exactly one factory matches, otherwise throw an `AmbiguousTableFactoryException` or `NoMatchingTableFactoryException`.
-
-The following example shows how to provide a custom streaming source with an additional `connector.debug` property flag for parameterization.
-
-
-
{% highlight java %}
-import org.apache.flink.table.sources.StreamTableSource;
+import org.apache.flink.api.common.serialization.DeserializationSchema;
+import org.apache.flink.api.common.typeinfo.TypeInformation;
+import org.apache.flink.table.connector.RuntimeConverter.Context;
+import org.apache.flink.table.connector.source.DynamicTableSource.DataStructureConverter;
+import org.apache.flink.table.data.RowData;
+import org.apache.flink.table.types.logical.LogicalType;
+import org.apache.flink.table.types.logical.LogicalTypeRoot;
import org.apache.flink.types.Row;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-class MySystemTableSourceFactory implements StreamTableSourceFactory {
-
- @Override
- public Map requiredContext() {
- Map context = new HashMap<>();
- context.put("update-mode", "append");
- context.put("connector.type", "my-system");
- return context;
+import org.apache.flink.types.RowKind;
+
+public class ChangelogCsvDeserializer implements DeserializationSchema {
+
+ private final List parsingTypes;
+ private final DataStructureConverter converter;
+ private final TypeInformation producedTypeInfo;
+ private final String columnDelimiter;
+
+ public ChangelogCsvDeserializer(
+ List parsingTypes,
+ DataStructureConverter converter,
+ TypeInformation producedTypeInfo,
+ String columnDelimiter) {
+ this.parsingTypes = parsingTypes;
+ this.converter = converter;
+ this.producedTypeInfo = producedTypeInfo;
+ this.columnDelimiter = columnDelimiter;
}
@Override
- public List supportedProperties() {
- List list = new ArrayList<>();
- list.add("connector.debug");
- return list;
+ public TypeInformation getProducedType() {
+ // return the type information required by Flink's core interfaces
+ return producedTypeInfo;
}
@Override
- public StreamTableSource createStreamTableSource(Map properties) {
- boolean isDebug = Boolean.valueOf(properties.get("connector.debug"));
-
- # additional validation of the passed properties can also happen here
-
- return new MySystemAppendTableSource(isDebug);
+ public void open(InitializationContext context) {
+ // converters must be open
+ converter.open(Context.create(ChangelogCsvDeserializer.class.getClassLoader()));
}
-}
-{% endhighlight %}
-
-
-
-{% highlight scala %}
-import java.util
-import org.apache.flink.table.sources.StreamTableSource
-import org.apache.flink.types.Row
-class MySystemTableSourceFactory extends StreamTableSourceFactory[Row] {
-
- override def requiredContext(): util.Map[String, String] = {
- val context = new util.HashMap[String, String]()
- context.put("update-mode", "append")
- context.put("connector.type", "my-system")
- context
+ @Override
+ public RowData deserialize(byte[] message) {
+ // parse the columns including a changelog flag
+ final String[] columns = new String(message).split(Pattern.quote(columnDelimiter));
+ final RowKind kind = RowKind.valueOf(columns[0]);
+ final Row row = new Row(kind, parsingTypes.size());
+ for (int i = 0; i < parsingTypes.size(); i++) {
+ row.setField(i, parse(parsingTypes.get(i).getTypeRoot(), columns[i + 1]));
+ }
+ // convert to internal data structure
+ return (RowData) converter.toInternal(row);
}
- override def supportedProperties(): util.List[String] = {
- val properties = new util.ArrayList[String]()
- properties.add("connector.debug")
- properties
+ private static Object parse(LogicalTypeRoot root, String value) {
+ switch (root) {
+ case INTEGER:
+ return Integer.parseInt(value);
+ case VARCHAR:
+ return value;
+ default:
+ throw new IllegalArgumentException();
+ }
}
- override def createStreamTableSource(properties: util.Map[String, String]): StreamTableSource[Row] = {
- val isDebug = java.lang.Boolean.valueOf(properties.get("connector.debug"))
-
- # additional validation of the passed properties can also happen here
-
- new MySystemAppendTableSource(isDebug)
+ @Override
+ public boolean isEndOfStream(RowData nextElement) {
+ return false;
}
}
{% endhighlight %}
-
-
-
-{% top %}
-
-### Use a TableFactory in the SQL Client
-
-In a SQL Client environment file, the previously presented factory could be declared as:
-
-{% highlight yaml %}
-tables:
- - name: MySystemTable
- type: source
- update-mode: append
- connector:
- type: my-system
- debug: true
-{% endhighlight %}
-
-The YAML file is translated into flattened string properties and a table factory is called with those properties that describe the connection to the external system:
-
-{% highlight text %}
-update-mode=append
-connector.type=my-system
-connector.debug=true
-{% endhighlight %}
-
-Attention Properties such as `tables.#.name` or `tables.#.type` are SQL Client specifics and are not passed to any factory. The `type` property decides, depending on the execution environment, whether a `BatchTableSourceFactory`/`StreamTableSourceFactory` (for `source`), a `BatchTableSinkFactory`/`StreamTableSinkFactory` (for `sink`), or both (for `both`) need to discovered.
-
-{% top %}
-
-### Use a TableFactory in the Table & SQL API
-
-For a type-safe, programmatic approach with explanatory Scaladoc/Javadoc, the Table & SQL API offers descriptors in `org.apache.flink.table.descriptors` that translate into string-based properties. See the [built-in descriptors](connect.html) for sources, sinks, and formats as a reference.
-
-
+**SocketSourceFunction**
-A custom descriptor can be defined by extending the `ConnectorDescriptor` class.
+The `SocketSourceFunction` opens a socket and consumes bytes. It splits records by the given byte
+delimiter (`\n` by default) and delegates the decoding to a pluggable `DeserializationSchema`. The
+source function can only work with a parallelism of 1.
{% highlight java %}
-import org.apache.flink.table.descriptors.ConnectorDescriptor;
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * Connector to MySystem with debug mode.
- */
-public class MySystemConnector extends ConnectorDescriptor {
-
- public final boolean isDebug;
-
- public MySystemConnector(boolean isDebug) {
- super("my-system", 1, false);
- this.isDebug = isDebug;
+import org.apache.flink.api.common.serialization.DeserializationSchema;
+import org.apache.flink.api.common.typeinfo.TypeInformation;
+import org.apache.flink.api.java.typeutils.ResultTypeQueryable;
+import org.apache.flink.configuration.Configuration;
+import org.apache.flink.streaming.api.functions.source.RichSourceFunction;
+import org.apache.flink.table.data.RowData;
+
+public class SocketSourceFunction extends RichSourceFunction implements ResultTypeQueryable {
+
+ private final String hostname;
+ private final int port;
+ private final byte byteDelimiter;
+ private final DeserializationSchema deserializer;
+
+ private volatile boolean isRunning = true;
+ private Socket currentSocket;
+
+ public SocketSourceFunction(String hostname, int port, byte byteDelimiter, DeserializationSchema deserializer) {
+ this.hostname = hostname;
+ this.port = port;
+ this.byteDelimiter = byteDelimiter;
+ this.deserializer = deserializer;
}
@Override
- protected Map toConnectorProperties() {
- Map properties = new HashMap<>();
- properties.put("connector.debug", Boolean.toString(isDebug));
- return properties;
+ public TypeInformation getProducedType() {
+ return deserializer.getProducedType();
}
-}
-{% endhighlight %}
-
-The descriptor can then be used to create a table with the table environment.
-{% highlight java %}
-StreamTableEnvironment tableEnv = // ...
-
-tableEnv
- .connect(new MySystemConnector(true))
- .withSchema(...)
- .inAppendMode()
- .createTemporaryTable("MySystemTable");
-{% endhighlight %}
-
-
-
-
-
-A custom descriptor can be defined by extending the `ConnectorDescriptor` class.
+ @Override
+ public void open(Configuration parameters) throws Exception {
+ deserializer.open(() -> getRuntimeContext().getMetricGroup());
+ }
-{% highlight scala %}
-import org.apache.flink.table.descriptors.ConnectorDescriptor
-import java.util.HashMap
-import java.util.Map
+ @Override
+ public void run(SourceContext ctx) throws Exception {
+ while (isRunning) {
+ // open and consume from socket
+ try (final Socket socket = new Socket()) {
+ currentSocket = socket;
+ socket.connect(new InetSocketAddress(hostname, port), 0);
+ try (InputStream stream = socket.getInputStream()) {
+ ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+ int b;
+ while ((b = stream.read()) >= 0) {
+ // buffer until delimiter
+ if (b != byteDelimiter) {
+ buffer.write(b);
+ }
+ // decode and emit record
+ else {
+ ctx.collect(deserializer.deserialize(buffer.toByteArray()));
+ buffer.reset();
+ }
+ }
+ }
+ } catch (Throwable t) {
+ t.printStackTrace(); // print and continue
+ }
+ Thread.sleep(1000);
+ }
+ }
-/**
- * Connector to MySystem with debug mode.
- */
-class MySystemConnector(isDebug: Boolean) extends ConnectorDescriptor("my-system", 1, false) {
-
- override protected def toConnectorProperties(): Map[String, String] = {
- val properties = new HashMap[String, String]
- properties.put("connector.debug", isDebug.toString)
- properties
+ @Override
+ public void cancel() {
+ isRunning = false;
+ try {
+ currentSocket.close();
+ } catch (Throwable t) {
+ // ignore
+ }
}
}
{% endhighlight %}
-The descriptor can then be used to create a table with the table environment.
-
-{% highlight scala %}
-val tableEnv: StreamTableEnvironment = // ...
-
-tableEnv
- .connect(new MySystemConnector(isDebug = true))
- .withSchema(...)
- .inAppendMode()
- .createTemporaryTable("MySystemTable")
-{% endhighlight %}
-
-
-
-
-
-You can use a Java `TableFactory` from Python using the `CustomConnectorDescriptor`.
-
-{% highlight python %}
-s_env = StreamExecutionEnvironment.get_execution_environment()
-st_env = StreamTableEnvironment.create(s_env)
-
-custom_connector = CustomConnectorDescriptor('my-system', 1, False)
-st_env\
- .connect(custom_connector.property("connector.debug", "true")) \
- .with_schema(...) \
- .in_append_mode()\
- .create_temporary_table("MySystemTable")
-{% endhighlight %}
-
-
-
-
{% top %}
diff --git a/docs/dev/table/sourceSinks.zh.md b/docs/dev/table/sourceSinks.zh.md
index e9f2c775959871..27f7b8e56c661b 100644
--- a/docs/dev/table/sourceSinks.zh.md
+++ b/docs/dev/table/sourceSinks.zh.md
@@ -22,796 +22,725 @@ specific language governing permissions and limitations
under the License.
-->
-A `TableSource` provides access to data which is stored in external systems (database, key-value store, message queue) or files. After a [TableSource is registered in a TableEnvironment](common.html#register-a-tablesource) it can be accessed by [Table API](tableApi.html) or [SQL]({{ site.baseurl }}/dev/table/sql/queries.html) queries.
+_Dynamic tables_ are the core concept of Flink's Table & SQL API for processing both bounded and unbounded
+data in a unified fashion.
-A `TableSink` [emits a Table](common.html#emit-a-table) to an external storage system, such as a database, key-value store, message queue, or file system (in different encodings, e.g., CSV, Parquet, or ORC).
+Because dynamic tables are only a logical concept, Flink does not own the data itself. Instead, the content
+of a dynamic table is stored in external systems (such as databases, key-value stores, message queues) or files.
-A `TableFactory` allows for separating the declaration of a connection to an external system from the actual implementation. A table factory creates configured instances of table sources and sinks from normalized, string-based properties. The properties can be generated programmatically using a `Descriptor` or via YAML configuration files for the [SQL Client](sqlClient.html).
+_Dynamic sources_ and _dynamic sinks_ can be used to read and write data from and to an external system. In
+the documentation, sources and sinks are often summarized under the term _connector_.
-Have a look at the [common concepts and API](common.html) page for details how to [register a TableSource](common.html#register-a-tablesource) and how to [emit a Table through a TableSink](common.html#emit-a-table). See the [built-in sources, sinks, and formats](connect.html) page for examples how to use factories.
+Flink provides pre-defined connectors for Kafka, Hive, and different file systems. See the [connector section]({% link dev/table/connectors/index.zh.md %})
+for more information about built-in table sources and sinks.
-* This will be replaced by the TOC
-{:toc}
+This page focuses on how to develop a custom, user-defined connector.
-Define a TableSource
---------------------
+Attention New table source and table sink interfaces have been
+introduced in Flink 1.11 as part of [FLIP-95](https://cwiki.apache.org/confluence/display/FLINK/FLIP-95%3A+New+TableSource+and+TableSink+interfaces).
+Also the factory interfaces have been reworked. FLIP-95 is not fully implemented yet. Many ability interfaces
+are not supported yet (e.g. for filter or partition push down). If necessary, please also have a look
+at the [old table sources and sinks page]({% link dev/table/legacySourceSinks.zh.md %}). Those interfaces
+are still supported for backwards compatibility.
-A `TableSource` is a generic interface that gives Table API and SQL queries access to data stored in an external system. It provides the schema of the table and the records that are mapped to rows with the table's schema. Depending on whether the `TableSource` is used in a streaming or batch query, the records are produced as a `DataSet` or `DataStream`.
+* This will be replaced by the TOC
+{:toc}
-If a `TableSource` is used in a streaming query it must implement the `StreamTableSource` interface, if it is used in a batch query it must implement the `BatchTableSource` interface. A `TableSource` can also implement both interfaces and be used in streaming and batch queries.
+Overview
+--------
-`StreamTableSource` and `BatchTableSource` extend the base interface `TableSource` that defines the following methods:
+In many cases, implementers don't need to create a new connector from scratch but would like to slightly
+modify existing connectors or hook into the existing stack. In other cases, implementers would like to
+create specialized connectors.
-
-
-{% highlight java %}
-TableSource {
+This section helps for both kinds of use cases. It explains the general architecture of table connectors
+from pure declaration in the API to runtime code that will be executed on the cluster.
- public TableSchema getTableSchema();
+The filled arrows show how objects are transformed to other objects from one stage to the next stage during
+the translation process.
- public TypeInformation getReturnType();
-
- public String explainSource();
-}
-{% endhighlight %}
+
+
-
-{% highlight scala %}
-TableSource[T] {
-
- def getTableSchema: TableSchema
+### Metadata
- def getReturnType: TypeInformation[T]
+Both Table API and SQL are declarative APIs. This includes the declaration of tables. Thus, executing
+a `CREATE TABLE` statement results in updated metadata in the target catalog.
- def explainSource: String
+For most catalog implementations, physical data in the external system is not modified for such an
+operation. Connector-specific dependencies don't have to be present in the classpath yet. The options declared
+in the `WITH` clause are neither validated nor otherwise interpreted.
-}
-{% endhighlight %}
-
-
-
-* `getTableSchema()`: Returns the schema of the produced table, i.e., the names and types of the fields of the table. The field types are defined using Flink's `DataType` (see [Table API types]({{ site.baseurl }}/dev/table/types.html) and [SQL types]({{ site.baseurl }}/dev/table/sql/index.html#data-types)). Note that the returned `TableSchema` shouldn't contain computed columns to reflect the schema of the physical `TableSource`.
+The metadata for dynamic tables (created via DDL or provided by the catalog) is represented as instances
+of `CatalogTable`. A table name will be resolved into a `CatalogTable` internally when necessary.
-* `getReturnType()`: Returns the physical type of the `DataStream` (`StreamTableSource`) or `DataSet` (`BatchTableSource`) and the records that are produced by the `TableSource`.
+### Planning
-* `explainSource()`: Returns a String that describes the `TableSource`. This method is optional and used for display purposes only.
+When it comes to planning and optimization of the table program, a `CatalogTable` needs to be resolved
+into a `DynamicTableSource` (for reading in a `SELECT` query) and `DynamicTableSink` (for writing in
+an `INSERT INTO` statement).
-The `TableSource` interface separates the logical table schema from the physical type of the returned `DataStream` or `DataSet`. As a consequence, all fields of the table schema (`getTableSchema()`) must be mapped to a field with corresponding type of the physical return type (`getReturnType()`). By default, this mapping is done based on field names. For example, a `TableSource` that defines a table schema with two fields `[name: String, size: Integer]` requires a `TypeInformation` with at least two fields called `name` and `size` of type `String` and `Integer`, respectively. This could be a `PojoTypeInfo` or a `RowTypeInfo` that have two fields named `name` and `size` with matching types.
+`DynamicTableSourceFactory` and `DynamicTableSinkFactory` provide connector-specific logic for translating
+the metadata of a `CatalogTable` into instances of `DynamicTableSource` and `DynamicTableSink`. In most
+of the cases, a factory's purpose is to validate options (such as `'port' = '5022'` in the example),
+configure encoding/decoding formats (if required), and create a parameterized instance of the table
+connector.
-However, some types, such as Tuple or CaseClass types, do support custom field names. If a `TableSource` returns a `DataStream` or `DataSet` of a type with fixed field names, it can implement the `DefinedFieldMapping` interface to map field names from the table schema to field names of the physical return type.
+By default, instances of `DynamicTableSourceFactory` and `DynamicTableSinkFactory` are discovered using
+Java's [Service Provider Interfaces (SPI)](https://docs.oracle.com/javase/tutorial/sound/SPI-intro.html). The
+`connector` option (such as `'connector' = 'custom'` in the example) must correspond to a valid factory
+identifier.
-### Defining a BatchTableSource
+Although it might not be apparent in the class naming, `DynamicTableSource` and `DynamicTableSink`
+can also be seen as stateful factories that eventually produce concrete runtime implementation for reading/writing
+the actual data.
-The `BatchTableSource` interface extends the `TableSource` interface and defines one additional method:
+The planner uses the source and sink instances to perform connector-specific bidirectional comunication
+until an optimal logical plan could be found. Depending on the optionally declared ability interfaces (e.g.
+`SupportsProjectionPushDown` or `SupportsOverwrite`), the planner might apply changes to an instance and
+thus mutate the produced runtime implementation.
-
-{% highlight scala %}
-BatchTableSource[T] extends TableSource[T] {
+Once the logical planning is complete, the planner will obtain the _runtime implementation_ from the table
+connector. Runtime logic is implemented in Flink's core connector interfaces such as `InputFormat` or `SourceFunction`.
- def getDataSet(execEnv: ExecutionEnvironment): DataSet[T]
-}
-{% endhighlight %}
-
-
+Those interfaces are grouped by another level of abstraction as subclasses of `ScanRuntimeProvider`,
+`LookupRuntimeProvider`, and `SinkRuntimeProvider`.
-* `getDataSet(execEnv)`: Returns a `DataSet` with the data of the table. The type of the `DataSet` must be identical to the return type defined by the `TableSource.getReturnType()` method. The `DataSet` can by created using a regular [data source]({{ site.baseurl }}/dev/batch/#data-sources) of the DataSet API. Commonly, a `BatchTableSource` is implemented by wrapping a `InputFormat` or [batch connector]({{ site.baseurl }}/dev/batch/connectors.html).
+For example, both `OutputFormatProvider` (providing `org.apache.flink.api.common.io.OutputFormat`) and `SinkFunctionProvider` (providing `org.apache.flink.streaming.api.functions.sink.SinkFunction`) are concrete instances of `SinkRuntimeProvider`
+that the planner can handle.
{% top %}
-### Defining a StreamTableSource
+Extension Points
+----------------
-The `StreamTableSource` interface extends the `TableSource` interface and defines one additional method:
+This section explains the available interfaces for extending Flink's table connectors.
-
+Dynamic table factories are used to configure a dynamic table connector for an external storage system from catalog
+and session information.
-
-{% highlight scala %}
-StreamTableSource[T] extends TableSource[T] {
+`org.apache.flink.table.factories.DynamicTableSourceFactory` can be implemented to construct a `DynamicTableSource`.
- def getDataStream(execEnv: StreamExecutionEnvironment): DataStream[T]
-}
-{% endhighlight %}
-
-
+`org.apache.flink.table.factories.DynamicTableSinkFactory` can be implemented to construct a `DynamicTableSink`.
-* `getDataStream(execEnv)`: Returns a `DataStream` with the data of the table. The type of the `DataStream` must be identical to the return type defined by the `TableSource.getReturnType()` method. The `DataStream` can by created using a regular [data source]({{ site.baseurl }}/dev/datastream_api.html#data-sources) of the DataStream API. Commonly, a `StreamTableSource` is implemented by wrapping a `SourceFunction` or a [stream connector]({{ site.baseurl }}/dev/connectors/).
+By default, the factory is discovered using the value of the `connector` option as the factory identifier
+and Java's Service Provider Interface.
-{% top %}
+In JAR files, references to new implementations can be added to the service file:
-### Defining a TableSource with Time Attributes
+`META-INF/services/org.apache.flink.table.factories.Factory`
-Time-based operations of streaming [Table API](tableApi.html#group-windows) and [SQL]({{ site.baseurl }}/dev/table/sql/queries.html#group-windows) queries, such as windowed aggregations or joins, require explicitly specified [time attributes](streaming/time_attributes.html).
+The framework will check for a single matching factory that is uniquely identified by factory identifier
+and requested base class (e.g. `DynamicTableSourceFactory`).
-A `TableSource` defines a time attribute as a field of type `Types.SQL_TIMESTAMP` in its table schema. In contrast to all regular fields in the schema, a time attribute must not be matched to a physical field in the return type of the table source. Instead, a `TableSource` defines a time attribute by implementing a certain interface.
+The factory discovery process can be bypassed by the catalog implementation if necessary. For this, a
+catalog needs to return an instance that implements the requested base class in `org.apache.flink.table.catalog.Catalog#getFactory`.
-#### Defining a Processing Time Attribute
+### Dynamic Table Source
-[Processing time attributes](streaming/time_attributes.html#processing-time) are commonly used in streaming queries. A processing time attribute returns the current wall-clock time of the operator that accesses it. A `TableSource` defines a processing time attribute by implementing the `DefinedProctimeAttribute` interface. The interface looks as follows:
+By definition, a dynamic table can change over time.
-
-
-{% highlight java %}
-DefinedProctimeAttribute {
+When reading a dynamic table, the content can either be considered as:
+- A changelog (finite or infinite) for which all changes are consumed continuously until the changelog
+ is exhausted. This is represented by the `ScanTableSource` interface.
+- A continuously changing or very large external table whose content is usually never read entirely
+ but queried for individual values when necessary. This is represented by the `LookupTableSource`
+ interface.
- public String getProctimeAttribute();
-}
-{% endhighlight %}
-
+A class can implement both of these interfaces at the same time. The planner decides about their usage depending
+on the specified query.
-
+A `ScanTableSource` scans all rows from an external storage system during runtime.
-* `getProctimeAttribute()`: Returns the name of the processing time attribute. The specified attribute must be defined of type `Types.SQL_TIMESTAMP` in the table schema and can be used in time-based operations. A `DefinedProctimeAttribute` table source can define no processing time attribute by returning `null`.
+The scanned rows don't have to contain only insertions but can also contain updates and deletions. Thus,
+the table source can be used to read a (finite or infinite) changelog. The returned _changelog mode_ indicates
+the set of changes that the planner can expect during runtime.
-Attention Both `StreamTableSource` and `BatchTableSource` can implement `DefinedProctimeAttribute` and define a processing time attribute. In case of a `BatchTableSource` the processing time field is initialized with the current timestamp during the table scan.
+For regular batch scenarios, the source can emit a bounded stream of insert-only rows.
-#### Defining a Rowtime Attribute
+For regular streaming scenarios, the source can emit an unbounded stream of insert-only rows.
-[Rowtime attributes](streaming/time_attributes.html#event-time) are attributes of type `TIMESTAMP` and handled in a unified way in stream and batch queries.
+For change data capture (CDC) scenarios, the source can emit bounded or unbounded streams with insert,
+update, and delete rows.
-A table schema field of type `SQL_TIMESTAMP` can be declared as rowtime attribute by specifying
+A table source can implement further abilitiy interfaces such as `SupportsProjectionPushDown` that might
+mutate an instance during planning. All abilities are listed in the `org.apache.flink.table.connector.source.abilities`
+package and in the documentation of `org.apache.flink.table.connector.source.ScanTableSource`.
-* the name of the field,
-* a `TimestampExtractor` that computes the actual value for the attribute (usually from one or more other fields), and
-* a `WatermarkStrategy` that specifies how watermarks are generated for the the rowtime attribute.
+The runtime implementation of a `ScanTableSource` must produce internal data structures. Thus, records
+must be emitted as `org.apache.flink.table.data.RowData`. The framework provides runtime converters such
+that a source can still work on common data structures and perform a conversion at the end.
-A `TableSource` defines a rowtime attribute by implementing the `DefinedRowtimeAttributes` interface. The interface looks as follows:
+#### Lookup Table Source
-
-
-{% highlight java %}
-DefinedRowtimeAttribute {
-
- public List getRowtimeAttributeDescriptors();
-}
-{% endhighlight %}
-
+A `LookupTableSource` looks up rows of an external storage system by one or more keys during runtime.
-
-{% highlight scala %}
-DefinedRowtimeAttributes {
+Compared to `ScanTableSource`, the source does not have to read the entire table and can lazily fetch individual
+values from a (possibly continuously changing) external table when necessary.
- def getRowtimeAttributeDescriptors: util.List[RowtimeAttributeDescriptor]
-}
-{% endhighlight %}
-
-
+Compared to `ScanTableSource`, a `LookupTableSource` does only support emitting insert-only changes currently.
-* `getRowtimeAttributeDescriptors()`: Returns a list of `RowtimeAttributeDescriptor`. A `RowtimeAttributeDescriptor` describes a rowtime attribute with the following properties:
- * `attributeName`: The name of the rowtime attribute in the table schema. The field must be defined with type `Types.SQL_TIMESTAMP`.
- * `timestampExtractor`: The timestamp extractor extracts the timestamp from a record with the return type. For example, it can convert a Long field into a timestamp or parse a String-encoded timestamp. Flink comes with a set of built-in `TimestampExtractor` implementation for common use cases. It is also possible to provide a custom implementation.
- * `watermarkStrategy`: The watermark strategy defines how watermarks are generated for the rowtime attribute. Flink comes with a set of built-in `WatermarkStrategy` implementations for common use cases. It is also possible to provide a custom implementation.
+Further abilities are not supported. See the documentation of `org.apache.flink.table.connector.source.LookupTableSource`
+for more information.
-Attention Although the `getRowtimeAttributeDescriptors()` method returns a list of descriptors, only a single rowtime attribute is support at the moment. We plan to remove this restriction in the future and support tables with more than one rowtime attribute.
+The runtime implementation of a `LookupTableSource` is a `TableFunction` or `AsyncTableFunction`. The function
+will be called with values for the given lookup keys during runtime.
-Attention Both, `StreamTableSource` and `BatchTableSource`, can implement `DefinedRowtimeAttributes` and define a rowtime attribute. In either case, the rowtime field is extracted using the `TimestampExtractor`. Hence, a `TableSource` that implements `StreamTableSource` and `BatchTableSource` and defines a rowtime attribute provides exactly the same data to streaming and batch queries.
+### Dynamic Table Sink
-##### Provided Timestamp Extractors
+By definition, a dynamic table can change over time.
-Flink provides `TimestampExtractor` implementations for common use cases.
+When writing a dynamic table, the content can always be considered as a changelog (finite or infinite)
+for which all changes are written out continuously until the changelog is exhausted. The returned _changelog mode_
+indicates the set of changes that the sink accepts during runtime.
-The following `TimestampExtractor` implementations are currently available:
+For regular batch scenarios, the sink can solely accept insert-only rows and write out bounded streams.
-* `ExistingField(fieldName)`: Extracts the value of a rowtime attribute from an existing `LONG`, `SQL_TIMESTAMP`, or timestamp formatted `STRING` field. One example of such a string would be '2018-05-28 12:34:56.000'.
-* `StreamRecordTimestamp()`: Extracts the value of a rowtime attribute from the timestamp of the `DataStream` `StreamRecord`. Note, this `TimestampExtractor` is not available for batch table sources.
+For regular streaming scenarios, the sink can solely accept insert-only rows and can write out unbounded streams.
-A custom `TimestampExtractor` can be defined by implementing the corresponding interface.
+For change data capture (CDC) scenarios, the sink can write out bounded or unbounded streams with insert,
+update, and delete rows.
-##### Provided Watermark Strategies
+A table sink can implement further abilitiy interfaces such as `SupportsOverwrite` that might mutate an
+instance during planning. All abilities are listed in the `org.apache.flink.table.connector.sink.abilities`
+package and in the documentation of `org.apache.flink.table.connector.sink.DynamicTableSink`.
-Flink provides `WatermarkStrategy` implementations for common use cases.
+The runtime implementation of a `DynamicTableSink` must consume internal data structures. Thus, records
+must be accepted as `org.apache.flink.table.data.RowData`. The framework provides runtime converters such
+that a sink can still work on common data structures and perform a conversion at the beginning.
-The following `WatermarkStrategy` implementations are currently available:
+### Encoding / Decoding Formats
-* `AscendingTimestamps`: A watermark strategy for ascending timestamps. Records with timestamps that are out-of-order will be considered late.
-* `BoundedOutOfOrderTimestamps(delay)`: A watermark strategy for timestamps that are at most out-of-order by the specified delay.
-* `PreserveWatermarks()`: A strategy which indicates the watermarks should be preserved from the underlying `DataStream`.
+Some table connectors accept different formats that encode and decode keys and/or values.
-A custom `WatermarkStrategy` can be defined by implementing the corresponding interface.
+Formats work similar to the pattern `DynamicTableSourceFactory -> DynamicTableSource -> ScanRuntimeProvider`,
+where the factory is responsible for translating options and the source is responsible for creating runtime logic.
-{% top %}
+Because formats might be located in different modules, they are discovered using Java's Service Provider
+Interface similar to [table factories](#dynamic-table-factories). In order to discover a format factory,
+the dynamic table factory searches for a factory that corresponds to a factory identifier and connector-specific
+base class.
-### Defining a TableSource with Projection Push-Down
+For example, the Kafka table source requires a `DeserializationSchema` as runtime interface for a decoding
+format. Therefore, the Kafka table source factory uses the value of the `value.format` option to discover
+a `DeserializationFormatFactory`.
-A `TableSource` supports projection push-down by implementing the `ProjectableTableSource` interface. The interface defines a single method:
+The following format factories are currently supported:
-
+The format factory translates the options into an `EncodingFormat` or a `DecodingFormat`. Those interfaces are
+another kind of factory that produce specialized format runtime logic for the given data type.
-
-
-* `projectFields(fields)`: Returns a *copy* of the `TableSource` with adjusted physical return type. The `fields` parameter provides the indexes of the fields that must be provided by the `TableSource`. The indexes relate to the `TypeInformation` of the physical return type, *not* to the logical table schema. The copied `TableSource` must adjust its return type and the returned `DataStream` or `DataSet`. The `TableSchema` of the copied `TableSource` must not be changed, i.e, it must be the same as the original `TableSource`. If the `TableSource` implements the `DefinedFieldMapping` interface, the field mapping must be adjusted to the new return type.
-
-Attention In order for Flink to distinguish a projection push-down table source from its original form, `explainSource` method must be override to include information regarding the projected fields.
-
-The `ProjectableTableSource` adds support to project flat fields. If the `TableSource` defines a table with nested schema, it can implement the `NestedFieldsProjectableTableSource` to extend the projection to nested fields. The `NestedFieldsProjectableTableSource` is defined as follows:
-
-
-
-* `projectNestedField(fields, nestedFields)`: Returns a *copy* of the `TableSource` with adjusted physical return type. Fields of the physical return type may be removed or reordered but their type must not be changed. The contract of this method is essentially the same as for the `ProjectableTableSource.projectFields()` method. In addition, the `nestedFields` parameter contains for each field index in the `fields` list, a list of paths to all nested fields that are accessed by the query. All other nested fields do not need to be read, parsed, and set in the records that are produced by the `TableSource`.
-
-Attention the types of the projected fields must not be changed but unused fields may be set to null or to a default value.
-
-{% top %}
-
-### Defining a TableSource with Filter Push-Down
-
-The `FilterableTableSource` interface adds support for filter push-down to a `TableSource`. A `TableSource` extending this interface is able to filter records such that the returned `DataStream` or `DataSet` returns fewer records.
-
-The interface looks as follows:
-
-
-
-* `applyPredicate(predicates)`: Returns a *copy* of the `TableSource` with added predicates. The `predicates` parameter is a mutable list of conjunctive predicates that are "offered" to the `TableSource`. The `TableSource` accepts to evaluate a predicate by removing it from the list. Predicates that are left in the list will be evaluated by a subsequent filter operator.
-* `isFilterPushedDown()`: Returns true if the `applyPredicate()` method was called before. Hence, `isFilterPushedDown()` must return true for all `TableSource` instances returned from a `applyPredicate()` call.
-
-Attention In order for Flink to distinguish a filter push-down table source from its original form, `explainSource` method must be override to include information regarding the push-down filters.
-
-{% top %}
-
-### Defining a TableSource for Lookups
-
-Attention This is an experimental feature. The interface may be changed in future versions. It's only supported in Blink planner.
-
-The `LookupableTableSource` interface adds support for the table to be accessed via key column(s) in a lookup fashion. This is very useful when used to join with a dimension table to enrich some information. If you want to use the `TableSource` in lookup mode, you should use the source in [temporal table join syntax](streaming/joins.html).
-
-The interface looks as follows:
-
-
-
-{% highlight java %}
-LookupableTableSource implements TableSource {
-
- public TableFunction getLookupFunction(String[] lookupkeys);
-
- public AsyncTableFunction getAsyncLookupFunction(String[] lookupkeys);
-
- public boolean isAsyncEnabled();
-}
-{% endhighlight %}
-
-
-* `getLookupFunction(lookupkeys)`: Returns a `TableFunction` which used to lookup the matched row(s) via lookup keys. The lookupkeys are the field names of `LookupableTableSource` in the join equal conditions. The eval method parameters of the returned `TableFunction`'s should be in the order which `lookupkeys` defined. It is recommended to define the parameters in varargs (e.g. `eval(Object... lookupkeys)` to match all the cases). The return type of the `TableFunction` must be identical to the return type defined by the `TableSource.getReturnType()` method.
-* `getAsyncLookupFunction(lookupkeys)`: Optional. Similar to `getLookupFunction`, but the `AsyncLookupFunction` lookups the matched row(s) asynchronously. The underlying of `AsyncLookupFunction` will be called via [Async I/O]({{ site.baseurl }}/dev/stream/operators/asyncio.html). The first argument of the eval method of the returned `AsyncTableFunction` should be defined as `java.util.concurrent.CompletableFuture` to collect results asynchronously (e.g. `eval(CompletableFuture> result, Object... lookupkeys)`). The implementation of this method can throw an exception if the TableSource doesn't support asynchronously lookup.
-* `isAsyncEnabled()`: Returns true if async lookup is enabled. It requires `getAsyncLookupFunction(lookupkeys)` is implemented if `isAsyncEnabled` returns true.
+For example, for a Kafka table source factory, the `DeserializationFormatFactory` would return an `EncodingFormat`
+that can be passed into the Kafka table source.
{% top %}
-Define a TableSink
+Full Stack Example
------------------
-A `TableSink` specifies how to emit a `Table` to an external system or location. The interface is generic such that it can support different storage locations and formats. There are different table sinks for batch tables and streaming tables.
+This section sketches how to implement a scan table source with a decoding format that supports changelog
+semantics. The example illustrates how all of the mentioned components play together. It can serve as
+a reference implementation.
-The general interface looks as follows:
+In particular, it shows how to
+- create factories that parse and validate options,
+- implement table connectors,
+- implement and discover custom formats,
+- and use provided utilities such as data structure converters and the `FactoryUtil`.
-
-
-{% highlight java %}
-TableSink {
-
- public TypeInformation getOutputType();
+The table source uses a simple single-threaded `SourceFunction` to open a socket that listens for incoming
+bytes. The raw bytes are decoded into rows by a pluggable format. The format expects a changelog flag
+as the first column.
- public String[] getFieldNames();
+We will use most of the interfaces metioned above to enable the following DDL:
- public TypeInformation[] getFieldTypes();
-
- public TableSink configure(String[] fieldNames, TypeInformation[] fieldTypes);
-}
+{% highlight sql %}
+CREATE TABLE UserScores (name STRING, score INT)
+WITH (
+ 'connector' = 'socket',
+ 'hostname' = 'localhost',
+ 'port' = '9999',
+ 'byte-delimiter' = '10',
+ 'format' = 'changelog-csv',
+ 'changelog-csv.column-delimiter' = '|'
+);
{% endhighlight %}
-
-
-
-{% highlight scala %}
-TableSink[T] {
-
- def getOutputType: TypeInformation
- def getFieldNames: Array[String]
+Because the format supports changelog semantics, we are able to ingest updates during runtime and create
+an updating view that can continuously evaluate changing data:
- def getFieldTypes: Array[TypeInformation]
-
- def configure(fieldNames: Array[String], fieldTypes: Array[TypeInformation]): TableSink[T]
-}
+{% highlight sql %}
+SELECT name, SUM(score) FROM UserScores GROUP BY name;
{% endhighlight %}
-
-
-
-The `TableSink#configure` method is called to pass the schema of the Table (field names and types) to emit to the `TableSink`. The method must return a new instance of the TableSink which is configured to emit the provided Table schema. Note that the provided `TableSchema` shouldn't contain computed columns to reflect the schema of the physical `TableSink`.
-
-### BatchTableSink
-Defines an external `TableSink` to emit a batch table.
-
-The interface looks as follows:
-
-
-
-{% highlight java %}
-BatchTableSink implements TableSink {
-
- public void emitDataSet(DataSet dataSet);
-}
+Use the following command to ingest data in a terminal:
+{% highlight text %}
+> nc -lk 9999
+INSERT|Alice|12
+INSERT|Bob|5
+DELETE|Alice|12
+INSERT|Alice|18
{% endhighlight %}
-
+### Factories
-{% top %}
+This section illustrates how to translate metadata coming from the catalog to concrete connector instances.
-### AppendStreamTableSink
+Both factories have been added to the `META-INF/services` directory.
-Defines an external `TableSink` to emit a streaming table with only insert changes.
+**`SocketDynamicTableFactory`**
-The interface looks as follows:
+The `SocketDynamicTableFactory` translates the catalog table to a table source. Because the table source
+requires a decoding format, we are discovering the format using the provided `FactoryUtil` for convenience.
-
-
{% highlight java %}
-AppendStreamTableSink implements TableSink {
+import org.apache.flink.api.common.serialization.DeserializationSchema;
+import org.apache.flink.configuration.ConfigOption;
+import org.apache.flink.configuration.ConfigOptions;
+import org.apache.flink.configuration.ReadableConfig;
+import org.apache.flink.table.connector.format.DecodingFormat;
+import org.apache.flink.table.connector.source.DynamicTableSource;
+import org.apache.flink.table.data.RowData;
+import org.apache.flink.table.factories.DeserializationFormatFactory;
+import org.apache.flink.table.factories.DynamicTableSourceFactory;
+import org.apache.flink.table.factories.FactoryUtil;
+import org.apache.flink.table.types.DataType;
+
+public class SocketDynamicTableFactory implements DynamicTableSourceFactory {
+
+ // define all options statically
+ public static final ConfigOption HOSTNAME = ConfigOptions.key("hostname")
+ .stringType()
+ .noDefaultValue();
+
+ public static final ConfigOption PORT = ConfigOptions.key("port")
+ .intType()
+ .noDefaultValue();
+
+ public static final ConfigOption BYTE_DELIMITER = ConfigOptions.key("byte-delimiter")
+ .intType()
+ .defaultValue(10); // corresponds to '\n'
- public DataStreamSink> consumeDataStream(DataStream dataStream);
-}
-{% endhighlight %}
-
+ @Override
+ public String factoryIdentifier() {
+ return "socket"; // used for matching to `connector = '...'`
+ }
-
-{% highlight scala %}
-AppendStreamTableSink[T] extends TableSink[T] {
+ @Override
+ public Set> requiredOptions() {
+ final Set> options = new HashSet<>();
+ options.add(HOSTNAME);
+ options.add(PORT);
+ options.add(FactoryUtil.FORMAT); // use pre-defined option for format
+ return options;
+ }
- def consumeDataStream(dataStream: DataStream[T]): DataStreamSink[_]
+ @Override
+ public Set> optionalOptions() {
+ final Set> options = new HashSet<>();
+ options.add(BYTE_DELIMITER);
+ return options;
+ }
+
+ @Override
+ public DynamicTableSource createDynamicTableSource(Context context) {
+ // either implement your custom validation logic here ...
+ // or use the provided helper utility
+ final FactoryUtil.TableFactoryHelper helper = FactoryUtil.createTableFactoryHelper(this, context);
+
+ // discover a suitable decoding format
+ final DecodingFormat> decodingFormat = helper.discoverDecodingFormat(
+ DeserializationFormatFactory.class,
+ FactoryUtil.FORMAT);
+
+ // validate all options
+ helper.validate();
+
+ // get the validated options
+ final ReadableConfig options = helper.getOptions();
+ final String hostname = options.get(HOSTNAME);
+ final int port = options.get(PORT);
+ final byte byteDelimiter = (byte) (int) options.get(BYTE_DELIMITER);
+
+ // derive the produced data type (excluding computed columns) from the catalog table
+ final DataType producedDataType = context.getCatalogTable().getSchema().toPhysicalRowDataType();
+
+ // create and return dynamic table source
+ return new SocketDynamicTableSource(hostname, port, byteDelimiter, decodingFormat, producedDataType);
+ }
}
{% endhighlight %}
-
-
-
-If the table is also modified by update or delete changes, a `TableException` will be thrown.
-{% top %}
+**`ChangelogCsvFormatFactory`**
-### RetractStreamTableSink
+The `ChangelogCsvFormatFactory` translates format-specific options to a format. The `FactoryUtil` in `SocketDynamicTableFactory`
+takes care of adapting the option keys accordingly and handles the prefixing like `changelog-csv.column-delimiter`.
-Defines an external `TableSink` to emit a streaming table with insert, update, and delete changes.
+Because this factory implements `DeserializationFormatFactory`, it could also be used for other connectors
+that support deserialization formats such as the Kafka connector.
-The interface looks as follows:
-
-
-{% highlight scala %}
-RetractStreamTableSink[T] extends TableSink[Tuple2[Boolean, T]] {
+ @Override
+ public Set> requiredOptions() {
+ return Collections.emptySet();
+ }
- def getRecordType: TypeInformation[T]
+ @Override
+ public Set> optionalOptions() {
+ final Set> options = new HashSet<>();
+ options.add(COLUMN_DELIMITER);
+ return options;
+ }
- def consumeDataStream(dataStream: DataStream[Tuple2[Boolean, T]]): DataStreamSink[_]
+ @Override
+ public DecodingFormat> createDecodingFormat(
+ DynamicTableFactory.Context context,
+ ReadableConfig formatOptions) {
+ // either implement your custom validation logic here ...
+ // or use the provided helper method
+ FactoryUtil.validateFactoryOptions(this, formatOptions);
+
+ // get the validated options
+ final String columnDelimiter = formatOptions.get(COLUMN_DELIMITER);
+
+ // create and return the format
+ return new ChangelogCsvFormat(columnDelimiter);
+ }
}
{% endhighlight %}
-
-
-The table will be converted into a stream of accumulate and retraction messages which are encoded as Java `Tuple2`. The first field is a boolean flag to indicate the message type (`true` indicates insert, `false` indicates delete). The second field holds the record of the requested type `T`.
+### Table Source and Decoding Format
-{% top %}
-
-### UpsertStreamTableSink
+This section illustrates how to translate from instances of the planning layer to runtime instances that
+are shipped to the cluster.
-Defines an external `TableSink` to emit a streaming table with insert, update, and delete changes.
+**`SocketDynamicTableSource`**
-The interface looks as follows:
+The `SocketDynamicTableSource` is used during planning. In our example, we don't implement any of the
+available ability interfaces. Therefore, the main logic can be found in `getScanRuntimeProvider(...)`
+where we instantiate the required `SourceFunction` and its `DeserializationSchema` for runtime. Both
+instances are parameterized to return internal data structures (i.e. `RowData`).
-
-
{% highlight java %}
-UpsertStreamTableSink implements TableSink> {
-
- public void setKeyFields(String[] keys);
+import org.apache.flink.api.common.serialization.DeserializationSchema;
+import org.apache.flink.streaming.api.functions.source.SourceFunction;
+import org.apache.flink.table.connector.ChangelogMode;
+import org.apache.flink.table.connector.format.DecodingFormat;
+import org.apache.flink.table.connector.source.DynamicTableSource;
+import org.apache.flink.table.connector.source.ScanTableSource;
+import org.apache.flink.table.connector.source.SourceFunctionProvider;
+import org.apache.flink.table.data.RowData;
+import org.apache.flink.table.types.DataType;
+
+public class SocketDynamicTableSource implements ScanTableSource {
+
+ private final String hostname;
+ private final int port;
+ private final byte byteDelimiter;
+ private final DecodingFormat> decodingFormat;
+ private final DataType producedDataType;
+
+ public SocketDynamicTableSource(
+ String hostname,
+ int port,
+ byte byteDelimiter,
+ DecodingFormat> decodingFormat,
+ DataType producedDataType) {
+ this.hostname = hostname;
+ this.port = port;
+ this.byteDelimiter = byteDelimiter;
+ this.decodingFormat = decodingFormat;
+ this.producedDataType = producedDataType;
+ }
- public void setIsAppendOnly(boolean isAppendOnly);
+ @Override
+ public ChangelogMode getChangelogMode() {
+ // in our example the format decides about the changelog mode
+ // but it could also be the source itself
+ return decodingFormat.getChangelogMode();
+ }
- public TypeInformation getRecordType();
+ @Override
+ public ScanRuntimeProvider getScanRuntimeProvider(ScanContext runtimeProviderContext) {
- public DataStreamSink> consumeDataStream(DataStream> dataStream);
-}
-{% endhighlight %}
-
+ // create runtime classes that are shipped to the cluster
-
-
-The table must be have unique key fields (atomic or composite) or be append-only. If the table does not have a unique key and is not append-only, a `TableException` will be thrown. The unique key of the table is configured by the `UpsertStreamTableSink#setKeyFields()` method.
-The table will be converted into a stream of upsert and delete messages which are encoded as a Java `Tuple2`. The first field is a boolean flag to indicate the message type. The second field holds the record of the requested type `T`.
+**`ChangelogCsvFormat`**
-A message with true boolean field is an upsert message for the configured key. A message with false flag is a delete message for the configured key. If the table is append-only, all messages will have a true flag and must be interpreted as insertions.
+The `ChangelogCsvFormat` is a decoding format that uses a `DeserializationSchema` during runtime. It
+supports emitting `INSERT` and `DELETE` changes.
-{% top %}
-
-Define a TableFactory
----------------------
-
-A `TableFactory` allows to create different table-related instances from string-based properties. All available factories are called for matching to the given set of properties and a corresponding factory class.
-
-Factories leverage Java's [Service Provider Interfaces (SPI)](https://docs.oracle.com/javase/tutorial/sound/SPI-intro.html) for discovering. This means that every dependency and JAR file should contain a file `org.apache.flink.table.factories.TableFactory` in the `META_INF/services` resource directory that lists all available table factories that it provides.
-
-Every table factory needs to implement the following interface:
-
-
-{% highlight scala %}
-package org.apache.flink.table.factories
-
-trait TableFactory {
+import org.apache.flink.api.common.serialization.DeserializationSchema;
+import org.apache.flink.api.common.typeinfo.TypeInformation;
+import org.apache.flink.table.connector.ChangelogMode;
+import org.apache.flink.table.connector.format.DecodingFormat;
+import org.apache.flink.table.connector.source.DynamicTableSource;
+import org.apache.flink.table.connector.source.DynamicTableSource.DataStructureConverter;
+import org.apache.flink.table.data.RowData;
+import org.apache.flink.table.types.DataType;
+import org.apache.flink.table.types.logical.LogicalType;
+import org.apache.flink.types.RowKind;
+
+public class ChangelogCsvFormat implements DecodingFormat> {
+
+ private final String columnDelimiter;
+
+ public ChangelogCsvFormat(String columnDelimiter) {
+ this.columnDelimiter = columnDelimiter;
+ }
- def requiredContext(): util.Map[String, String]
+ @Override
+ @SuppressWarnings("unchecked")
+ public DeserializationSchema createRuntimeDecoder(
+ DynamicTableSource.Context context,
+ DataType producedDataType) {
+ // create type information for the DeserializationSchema
+ final TypeInformation producedTypeInfo = (TypeInformation) context.createTypeInformation(
+ producedDataType);
+
+ // most of the code in DeserializationSchema will not work on internal data structures
+ // create a converter for conversion at the end
+ final DataStructureConverter converter = context.createDataStructureConverter(producedDataType);
+
+ // use logical types during runtime for parsing
+ final List parsingTypes = producedDataType.getLogicalType().getChildren();
+
+ // create runtime class
+ return new ChangelogCsvDeserializer(parsingTypes, converter, producedTypeInfo, columnDelimiter);
+ }
- def supportedProperties(): util.List[String]
+ @Override
+ public ChangelogMode getChangelogMode() {
+ // define that this format can produce INSERT and DELETE rows
+ return ChangelogMode.newBuilder()
+ .addContainedKind(RowKind.INSERT)
+ .addContainedKind(RowKind.DELETE)
+ .build();
+ }
}
{% endhighlight %}
-
-
-
-* `requiredContext()`: Specifies the context that this factory has been implemented for. The framework guarantees to only match for this factory if the specified set of properties and values are met. Typical properties might be `connector.type`, `format.type`, or `update-mode`. Property keys such as `connector.property-version` and `format.property-version` are reserved for future backwards compatibility cases.
-* `supportedProperties()`: List of property keys that this factory can handle. This method will be used for validation. If a property is passed that this factory cannot handle, an exception will be thrown. The list must not contain the keys that are specified by the context.
-In order to create a specific instance, a factory class can implement one or more interfaces provided in `org.apache.flink.table.factories`:
+### Runtime
-* `BatchTableSourceFactory`: Creates a batch table source.
-* `BatchTableSinkFactory`: Creates a batch table sink.
-* `StreamTableSourceFactory`: Creates a stream table source.
-* `StreamTableSinkFactory`: Creates a stream table sink.
-* `DeserializationSchemaFactory`: Creates a deserialization schema format.
-* `SerializationSchemaFactory`: Creates a serialization schema format.
+For completeness, this section illustrates the runtime logic for both `SourceFunction` and `DeserializationSchema`.
-The discovery of a factory happens in multiple stages:
+**ChangelogCsvDeserializer**
-- Discover all available factories.
-- Filter by factory class (e.g., `StreamTableSourceFactory`).
-- Filter by matching context.
-- Filter by supported properties.
-- Verify that exactly one factory matches, otherwise throw an `AmbiguousTableFactoryException` or `NoMatchingTableFactoryException`.
+The `ChangelogCsvDeserializer` contains a simple parsing logic for converting bytes into `Row` of `Integer`
+and `String` with a row kind. The final conversion step converts those into internal data structures.
-The following example shows how to provide a custom streaming source with an additional `connector.debug` property flag for parameterization.
-
-
-
{% highlight java %}
-import org.apache.flink.table.sources.StreamTableSource;
+import org.apache.flink.api.common.serialization.DeserializationSchema;
+import org.apache.flink.api.common.typeinfo.TypeInformation;
+import org.apache.flink.table.connector.RuntimeConverter.Context;
+import org.apache.flink.table.connector.source.DynamicTableSource.DataStructureConverter;
+import org.apache.flink.table.data.RowData;
+import org.apache.flink.table.types.logical.LogicalType;
+import org.apache.flink.table.types.logical.LogicalTypeRoot;
import org.apache.flink.types.Row;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-class MySystemTableSourceFactory implements StreamTableSourceFactory {
-
- @Override
- public Map requiredContext() {
- Map context = new HashMap<>();
- context.put("update-mode", "append");
- context.put("connector.type", "my-system");
- return context;
+import org.apache.flink.types.RowKind;
+
+public class ChangelogCsvDeserializer implements DeserializationSchema {
+
+ private final List parsingTypes;
+ private final DataStructureConverter converter;
+ private final TypeInformation producedTypeInfo;
+ private final String columnDelimiter;
+
+ public ChangelogCsvDeserializer(
+ List parsingTypes,
+ DataStructureConverter converter,
+ TypeInformation producedTypeInfo,
+ String columnDelimiter) {
+ this.parsingTypes = parsingTypes;
+ this.converter = converter;
+ this.producedTypeInfo = producedTypeInfo;
+ this.columnDelimiter = columnDelimiter;
}
@Override
- public List supportedProperties() {
- List list = new ArrayList<>();
- list.add("connector.debug");
- return list;
+ public TypeInformation getProducedType() {
+ // return the type information required by Flink's core interfaces
+ return producedTypeInfo;
}
@Override
- public StreamTableSource createStreamTableSource(Map properties) {
- boolean isDebug = Boolean.valueOf(properties.get("connector.debug"));
-
- # additional validation of the passed properties can also happen here
-
- return new MySystemAppendTableSource(isDebug);
+ public void open(InitializationContext context) {
+ // converters must be open
+ converter.open(Context.create(ChangelogCsvDeserializer.class.getClassLoader()));
}
-}
-{% endhighlight %}
-
-
-{% highlight scala %}
-import java.util
-import org.apache.flink.table.sources.StreamTableSource
-import org.apache.flink.types.Row
-
-class MySystemTableSourceFactory extends StreamTableSourceFactory[Row] {
-
- override def requiredContext(): util.Map[String, String] = {
- val context = new util.HashMap[String, String]()
- context.put("update-mode", "append")
- context.put("connector.type", "my-system")
- context
+ @Override
+ public RowData deserialize(byte[] message) {
+ // parse the columns including a changelog flag
+ final String[] columns = new String(message).split(Pattern.quote(columnDelimiter));
+ final RowKind kind = RowKind.valueOf(columns[0]);
+ final Row row = new Row(kind, parsingTypes.size());
+ for (int i = 0; i < parsingTypes.size(); i++) {
+ row.setField(i, parse(parsingTypes.get(i).getTypeRoot(), columns[i + 1]));
+ }
+ // convert to internal data structure
+ return (RowData) converter.toInternal(row);
}
- override def supportedProperties(): util.List[String] = {
- val properties = new util.ArrayList[String]()
- properties.add("connector.debug")
- properties
+ private static Object parse(LogicalTypeRoot root, String value) {
+ switch (root) {
+ case INTEGER:
+ return Integer.parseInt(value);
+ case VARCHAR:
+ return value;
+ default:
+ throw new IllegalArgumentException();
+ }
}
- override def createStreamTableSource(properties: util.Map[String, String]): StreamTableSource[Row] = {
- val isDebug = java.lang.Boolean.valueOf(properties.get("connector.debug"))
-
- # additional validation of the passed properties can also happen here
-
- new MySystemAppendTableSource(isDebug)
+ @Override
+ public boolean isEndOfStream(RowData nextElement) {
+ return false;
}
}
{% endhighlight %}
-
-
-
-{% top %}
-
-### Use a TableFactory in the SQL Client
-
-In a SQL Client environment file, the previously presented factory could be declared as:
-
-{% highlight yaml %}
-tables:
- - name: MySystemTable
- type: source
- update-mode: append
- connector:
- type: my-system
- debug: true
-{% endhighlight %}
-
-The YAML file is translated into flattened string properties and a table factory is called with those properties that describe the connection to the external system:
-
-{% highlight text %}
-update-mode=append
-connector.type=my-system
-connector.debug=true
-{% endhighlight %}
-
-Attention Properties such as `tables.#.name` or `tables.#.type` are SQL Client specifics and are not passed to any factory. The `type` property decides, depending on the execution environment, whether a `BatchTableSourceFactory`/`StreamTableSourceFactory` (for `source`), a `BatchTableSinkFactory`/`StreamTableSinkFactory` (for `sink`), or both (for `both`) need to discovered.
-
-{% top %}
-### Use a TableFactory in the Table & SQL API
+**SocketSourceFunction**
-For a type-safe, programmatic approach with explanatory Scaladoc/Javadoc, the Table & SQL API offers descriptors in `org.apache.flink.table.descriptors` that translate into string-based properties. See the [built-in descriptors](connect.html) for sources, sinks, and formats as a reference.
-
-
-
-
-A custom descriptor can be defined by extending the `ConnectorDescriptor` class.
+The `SocketSourceFunction` opens a socket and consumes bytes. It splits records by the given byte
+delimiter (`\n` by default) and delegates the decoding to a pluggable `DeserializationSchema`. The
+source function can only work with a parallelism of 1.
{% highlight java %}
-import org.apache.flink.table.descriptors.ConnectorDescriptor;
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * Connector to MySystem with debug mode.
- */
-public class MySystemConnector extends ConnectorDescriptor {
-
- public final boolean isDebug;
-
- public MySystemConnector(boolean isDebug) {
- super("my-system", 1, false);
- this.isDebug = isDebug;
+import org.apache.flink.api.common.serialization.DeserializationSchema;
+import org.apache.flink.api.common.typeinfo.TypeInformation;
+import org.apache.flink.api.java.typeutils.ResultTypeQueryable;
+import org.apache.flink.configuration.Configuration;
+import org.apache.flink.streaming.api.functions.source.RichSourceFunction;
+import org.apache.flink.table.data.RowData;
+
+public class SocketSourceFunction extends RichSourceFunction implements ResultTypeQueryable {
+
+ private final String hostname;
+ private final int port;
+ private final byte byteDelimiter;
+ private final DeserializationSchema deserializer;
+
+ private volatile boolean isRunning = true;
+ private Socket currentSocket;
+
+ public SocketSourceFunction(String hostname, int port, byte byteDelimiter, DeserializationSchema deserializer) {
+ this.hostname = hostname;
+ this.port = port;
+ this.byteDelimiter = byteDelimiter;
+ this.deserializer = deserializer;
}
@Override
- protected Map toConnectorProperties() {
- Map properties = new HashMap<>();
- properties.put("connector.debug", Boolean.toString(isDebug));
- return properties;
+ public TypeInformation getProducedType() {
+ return deserializer.getProducedType();
}
-}
-{% endhighlight %}
-
-The descriptor can then be used to create a table with the table environment.
-
-{% highlight java %}
-StreamTableEnvironment tableEnv = // ...
-
-tableEnv
- .connect(new MySystemConnector(true))
- .withSchema(...)
- .inAppendMode()
- .createTemporaryTable("MySystemTable");
-{% endhighlight %}
-
-
-
-
-A custom descriptor can be defined by extending the `ConnectorDescriptor` class.
-
-{% highlight scala %}
-import org.apache.flink.table.descriptors.ConnectorDescriptor
-import java.util.HashMap
-import java.util.Map
+ @Override
+ public void open(Configuration parameters) throws Exception {
+ deserializer.open(() -> getRuntimeContext().getMetricGroup());
+ }
-/**
- * Connector to MySystem with debug mode.
- */
-class MySystemConnector(isDebug: Boolean) extends ConnectorDescriptor("my-system", 1, false) {
+ @Override
+ public void run(SourceContext ctx) throws Exception {
+ while (isRunning) {
+ // open and consume from socket
+ try (final Socket socket = new Socket()) {
+ currentSocket = socket;
+ socket.connect(new InetSocketAddress(hostname, port), 0);
+ try (InputStream stream = socket.getInputStream()) {
+ ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+ int b;
+ while ((b = stream.read()) >= 0) {
+ // buffer until delimiter
+ if (b != byteDelimiter) {
+ buffer.write(b);
+ }
+ // decode and emit record
+ else {
+ ctx.collect(deserializer.deserialize(buffer.toByteArray()));
+ buffer.reset();
+ }
+ }
+ }
+ } catch (Throwable t) {
+ t.printStackTrace(); // print and continue
+ }
+ Thread.sleep(1000);
+ }
+ }
- override protected def toConnectorProperties(): Map[String, String] = {
- val properties = new HashMap[String, String]
- properties.put("connector.debug", isDebug.toString)
- properties
+ @Override
+ public void cancel() {
+ isRunning = false;
+ try {
+ currentSocket.close();
+ } catch (Throwable t) {
+ // ignore
+ }
}
}
{% endhighlight %}
-The descriptor can then be used to create a table with the table environment.
-
-{% highlight scala %}
-val tableEnv: StreamTableEnvironment = // ...
-
-tableEnv
- .connect(new MySystemConnector(isDebug = true))
- .withSchema(...)
- .inAppendMode()
- .createTemporaryTable("MySystemTable")
-{% endhighlight %}
-
-
-
-
-
-You can use a Java `TableFactory` from Python using the `CustomConnectorDescriptor`.
-
-{% highlight python %}
-s_env = StreamExecutionEnvironment.get_execution_environment()
-st_env = StreamTableEnvironment.create(s_env)
-
-custom_connector = CustomConnectorDescriptor('my-system', 1, False)
-st_env\
- .connect(custom_connector.property("connector.debug", "true")) \
- .with_schema(...) \
- .in_append_mode()\
- .create_temporary_table("MySystemTable")
-{% endhighlight %}
-
-
-
-
{% top %}
diff --git a/docs/dev/table/sql/alter.md b/docs/dev/table/sql/alter.md
index 4cba0828e1d345..61d90981300b74 100644
--- a/docs/dev/table/sql/alter.md
+++ b/docs/dev/table/sql/alter.md
@@ -35,7 +35,7 @@ Flink SQL supports the following ALTER statements for now:
## Run an ALTER statement
-ALTER statements can be executed with the `sqlUpdate()` method of the `TableEnvironment`, or executed in [SQL CLI]({{ site.baseurl }}/dev/table/sqlClient.html). The `sqlUpdate()` method returns nothing for a successful ALTER operation, otherwise will throw an exception.
+ALTER statements can be executed with the `executeSql()` method of the `TableEnvironment`, or executed in [SQL CLI]({{ site.baseurl }}/dev/table/sqlClient.html). The `executeSql()` method returns 'OK' for a successful ALTER operation, otherwise will throw an exception.
The following examples show how to run an ALTER statement in `TableEnvironment` and in SQL CLI.
@@ -46,16 +46,18 @@ EnvironmentSettings settings = EnvironmentSettings.newInstance()...
TableEnvironment tableEnv = TableEnvironment.create(settings);
// register a table named "Orders"
-tableEnv.sqlUpdate("CREATE TABLE Orders (`user` BIGINT, product STRING, amount INT) WITH (...)");
+tableEnv.executeSql("CREATE TABLE Orders (`user` BIGINT, product STRING, amount INT) WITH (...)");
// a string array: ["Orders"]
-String[] tables = tableEnv.listTable();
+String[] tables = tableEnv.listTables();
+// or tableEnv.executeSql("SHOW TABLES").print();
// rename "Orders" to "NewOrders"
-tableEnv.sqlUpdate("ALTER TABLE Orders RENAME TO NewOrders;");
+tableEnv.executeSql("ALTER TABLE Orders RENAME TO NewOrders;");
// a string array: ["NewOrders"]
-String[] tables = tableEnv.listTable();
+String[] tables = tableEnv.listTables();
+// or tableEnv.executeSql("SHOW TABLES").print();
{% endhighlight %}
@@ -65,32 +67,36 @@ val settings = EnvironmentSettings.newInstance()...
val tableEnv = TableEnvironment.create(settings)
// register a table named "Orders"
-tableEnv.sqlUpdate("CREATE TABLE Orders (`user` BIGINT, product STRING, amount INT) WITH (...)");
+tableEnv.executeSql("CREATE TABLE Orders (`user` BIGINT, product STRING, amount INT) WITH (...)");
// a string array: ["Orders"]
-val tables = tableEnv.listTable()
+val tables = tableEnv.listTables()
+// or tableEnv.executeSql("SHOW TABLES").print()
// rename "Orders" to "NewOrders"
-tableEnv.sqlUpdate("ALTER TABLE Orders RENAME TO NewOrders;")
+tableEnv.executeSql("ALTER TABLE Orders RENAME TO NewOrders;")
// a string array: ["NewOrders"]
-val tables = tableEnv.listTable()
+val tables = tableEnv.listTables()
+// or tableEnv.executeSql("SHOW TABLES").print()
{% endhighlight %}
{% highlight python %}
-settings = EnvironmentSettings.newInstance()...
-table_env = TableEnvironment.create(settings)
+settings = EnvironmentSettings.new_instance()...
+table_env = StreamTableEnvironment.create(env, settings)
# a string array: ["Orders"]
-tables = tableEnv.listTable()
+tables = table_env.list_tables()
+# or table_env.execute_sql("SHOW TABLES").print()
# rename "Orders" to "NewOrders"
-tableEnv.sqlUpdate("ALTER TABLE Orders RENAME TO NewOrders;")
+table_env.execute_sql("ALTER TABLE Orders RENAME TO NewOrders;")
# a string array: ["NewOrders"]
-tables = tableEnv.listTable()
+tables = table_env.list_tables()
+# or table_env.execute_sql("SHOW TABLES").print()
{% endhighlight %}
@@ -142,10 +148,14 @@ Set one or more properties in the specified database. If a particular property i
{% highlight sql%}
ALTER [TEMPORARY|TEMPORARY SYSTEM] FUNCTION
[IF EXISTS] [catalog_name.][db_name.]function_name
- AS identifier [LANGUAGE JAVA|SCALA|
+ AS identifier [LANGUAGE JAVA|SCALA|PYTHON]
{% endhighlight %}
-Alter a catalog function with the new identifier which is full classpath for JAVA/SCALA and optional language tag. If a function doesn't exist in the catalog, an exception is thrown.
+Alter a catalog function with the new identifier and optional language tag. If a function doesn't exist in the catalog, an exception is thrown.
+
+If the language tag is JAVA/SCALA, the identifier is the full classpath of the UDF. For the implementation of Java/Scala UDF, please refer to [User-defined Functions]({{ site.baseurl }}/dev/table/functions/udfs.html) for more details.
+
+If the language tag is PYTHON, the identifier is the fully qualified name of the UDF, e.g. `pyflink.table.tests.test_udf.add`. For the implementation of Python UDF, please refer to [Python UDFs]({{ site.baseurl }}/dev/table/python/python_udfs.html) for more details.
**TEMPORARY**
@@ -159,7 +169,7 @@ Alter temporary system function that has no namespace and overrides built-in fun
If the function doesn't exist, nothing happens.
-**LANGUAGE JAVA\|SCALA**
+**LANGUAGE JAVA\|SCALA\|PYTHON**
-Language tag to instruct flink runtime how to execute the function. Currently only JAVA and SCALA are supported, the default language for a function is JAVA.
+Language tag to instruct flink runtime how to execute the function. Currently only JAVA, SCALA and PYTHON are supported, the default language for a function is JAVA.
diff --git a/docs/dev/table/sql/alter.zh.md b/docs/dev/table/sql/alter.zh.md
index def2862c2f03c1..7d3eed1b4306e4 100644
--- a/docs/dev/table/sql/alter.zh.md
+++ b/docs/dev/table/sql/alter.zh.md
@@ -35,7 +35,7 @@ Flink SQL 目前支持以下 ALTER 语句:
## 执行 ALTER 语句
-可以使用 `TableEnvironment` 中的 `sqlUpdate()` 方法执行 ALTER 语句,也可以在 [SQL CLI]({{ site.baseurl }}/zh/dev/table/sqlClient.html) 中执行 ALTER 语句。 若 ALTER 操作执行成功,`sqlUpdate()` 方法不返回任何内容,否则会抛出异常。
+可以使用 `TableEnvironment` 中的 `executeSql()` 方法执行 ALTER 语句,也可以在 [SQL CLI]({{ site.baseurl }}/zh/dev/table/sqlClient.html) 中执行 ALTER 语句。 若 ALTER 操作执行成功,`executeSql()` 方法返回 'OK',否则会抛出异常。
以下的例子展示了如何在 `TableEnvironment` 和 SQL CLI 中执行一个 ALTER 语句。
@@ -46,16 +46,18 @@ EnvironmentSettings settings = EnvironmentSettings.newInstance()...
TableEnvironment tableEnv = TableEnvironment.create(settings);
// 注册名为 “Orders” 的表
-tableEnv.sqlUpdate("CREATE TABLE Orders (`user` BIGINT, product STRING, amount INT) WITH (...)");
+tableEnv.executeSql("CREATE TABLE Orders (`user` BIGINT, product STRING, amount INT) WITH (...)");
// 字符串数组: ["Orders"]
-String[] tables = tableEnv.listTable();
+String[] tables = tableEnv.listTables();
+// or tableEnv.executeSql("SHOW TABLES").print();
// 把 “Orders” 的表名改为 “NewOrders”
-tableEnv.sqlUpdate("ALTER TABLE Orders RENAME TO NewOrders;");
+tableEnv.executeSql("ALTER TABLE Orders RENAME TO NewOrders;");
// 字符串数组:["NewOrders"]
-String[] tables = tableEnv.listTable();
+String[] tables = tableEnv.listTables();
+// or tableEnv.executeSql("SHOW TABLES").print();
{% endhighlight %}
@@ -142,10 +148,14 @@ ALTER DATABASE [catalog_name.]db_name SET (key1=val1, key2=val2, ...)
{% highlight sql%}
ALTER [TEMPORARY|TEMPORARY SYSTEM] FUNCTION
[IF EXISTS] [catalog_name.][db_name.]function_name
- AS identifier [LANGUAGE JAVA|SCALA|
+ AS identifier [LANGUAGE JAVA|SCALA|PYTHON]
{% endhighlight %}
-修改一个有 catalog 和数据库命名空间的 catalog function ,其需要指定 JAVA / SCALA 或其他 language tag 完整的 classpath。若函数不存在,删除会抛出异常。
+修改一个有 catalog 和数据库命名空间的 catalog function ,需要指定一个新的 identifier ,可指定 language tag 。若函数不存在,删除会抛出异常。
+
+如果 language tag 是 JAVA 或者 SCALA ,则 identifier 是 UDF 实现类的全限定名。关于 JAVA/SCALA UDF 的实现,请参考 [自定义函数]({{ site.baseurl }}/zh/dev/table/functions/udfs.html)。
+
+如果 language tag 是 PYTHON , 则 identifier 是 UDF 对象的全限定名,例如 `pyflink.table.tests.test_udf.add`。关于 PYTHON UDF 的实现,请参考 [Python UDFs]({{ site.baseurl }}/zh/dev/table/python/python_udfs.html)。
**TEMPORARY**
@@ -159,6 +169,6 @@ ALTER [TEMPORARY|TEMPORARY SYSTEM] FUNCTION
若函数不存在,则不进行任何操作。
-**LANGUAGE JAVA\|SCALA**
+**LANGUAGE JAVA\|SCALA\|PYTHON**
-Language tag 用于指定 Flink runtime 如何执行这个函数。目前,只支持 JAVA 和 SCALA,且函数的默认语言为 JAVA。
\ No newline at end of file
+Language tag 用于指定 Flink runtime 如何执行这个函数。目前,只支持 JAVA,SCALA 和 PYTHON,且函数的默认语言为 JAVA。
diff --git a/docs/dev/table/sql/create.md b/docs/dev/table/sql/create.md
index e44e2bc74e0bde..4bc398c5d79002 100644
--- a/docs/dev/table/sql/create.md
+++ b/docs/dev/table/sql/create.md
@@ -31,11 +31,12 @@ Flink SQL supports the following CREATE statements for now:
- CREATE TABLE
- CREATE DATABASE
+- CREATE VIEW
- CREATE FUNCTION
## Run a CREATE statement
-CREATE statements can be executed with the `sqlUpdate()` method of the `TableEnvironment`, or executed in [SQL CLI]({{ site.baseurl }}/dev/table/sqlClient.html). The `sqlUpdate()` method returns nothing for a successful CREATE operation, otherwise will throw an exception.
+CREATE statements can be executed with the `executeSql()` method of the `TableEnvironment`, or executed in [SQL CLI]({{ site.baseurl }}/dev/table/sqlClient.html). The `executeSql()` method returns 'OK' for a successful CREATE operation, otherwise will throw an exception.
The following examples show how to run a CREATE statement in `TableEnvironment` and in SQL CLI.
@@ -47,16 +48,16 @@ TableEnvironment tableEnv = TableEnvironment.create(settings);
// SQL query with a registered table
// register a table named "Orders"
-tableEnv.sqlUpdate("CREATE TABLE Orders (`user` BIGINT, product STRING, amount INT) WITH (...)");
+tableEnv.executeSql("CREATE TABLE Orders (`user` BIGINT, product STRING, amount INT) WITH (...)");
// run a SQL query on the Table and retrieve the result as a new Table
Table result = tableEnv.sqlQuery(
"SELECT product, amount FROM Orders WHERE product LIKE '%Rubber%'");
-// SQL update with a registered table
+// Execute insert SQL with a registered table
// register a TableSink
-tableEnv.sqlUpdate("CREATE TABLE RubberOrders(product STRING, amount INT) WITH (...)");
-// run a SQL update query on the Table and emit the result to the TableSink
-tableEnv.sqlUpdate(
+tableEnv.executeSql("CREATE TABLE RubberOrders(product STRING, amount INT) WITH (...)");
+// run an insert SQL on the Table and emit the result to the TableSink
+tableEnv.executeSql(
"INSERT INTO RubberOrders SELECT product, amount FROM Orders WHERE product LIKE '%Rubber%'");
{% endhighlight %}
@@ -68,38 +69,38 @@ val tableEnv = TableEnvironment.create(settings)
// SQL query with a registered table
// register a table named "Orders"
-tableEnv.sqlUpdate("CREATE TABLE Orders (`user` BIGINT, product STRING, amount INT) WITH (...)");
+tableEnv.executeSql("CREATE TABLE Orders (`user` BIGINT, product STRING, amount INT) WITH (...)");
// run a SQL query on the Table and retrieve the result as a new Table
val result = tableEnv.sqlQuery(
"SELECT product, amount FROM Orders WHERE product LIKE '%Rubber%'");
-// SQL update with a registered table
+// Execute insert SQL with a registered table
// register a TableSink
-tableEnv.sqlUpdate("CREATE TABLE RubberOrders(product STRING, amount INT) WITH ('connector.path'='/path/to/file' ...)");
-// run a SQL update query on the Table and emit the result to the TableSink
-tableEnv.sqlUpdate(
+tableEnv.executeSql("CREATE TABLE RubberOrders(product STRING, amount INT) WITH ('connector.path'='/path/to/file' ...)");
+// run an insert SQL on the Table and emit the result to the TableSink
+tableEnv.executeSql(
"INSERT INTO RubberOrders SELECT product, amount FROM Orders WHERE product LIKE '%Rubber%'")
{% endhighlight %}
{% highlight python %}
-settings = EnvironmentSettings.newInstance()...
-table_env = TableEnvironment.create(settings)
+settings = EnvironmentSettings.new_instance()...
+table_env = StreamTableEnvironment.create(env, settings)
# SQL query with a registered table
# register a table named "Orders"
-tableEnv.sqlUpdate("CREATE TABLE Orders (`user` BIGINT, product STRING, amount INT) WITH (...)");
+table_env.execute_sql("CREATE TABLE Orders (`user` BIGINT, product STRING, amount INT) WITH (...)");
# run a SQL query on the Table and retrieve the result as a new Table
-result = tableEnv.sqlQuery(
+result = table_env.sql_query(
"SELECT product, amount FROM Orders WHERE product LIKE '%Rubber%'");
-# SQL update with a registered table
+# Execute an INSERT SQL with a registered table
# register a TableSink
-table_env.sql_update("CREATE TABLE RubberOrders(product STRING, amount INT) WITH (...)")
-# run a SQL update query on the Table and emit the result to the TableSink
+table_env.execute_sql("CREATE TABLE RubberOrders(product STRING, amount INT) WITH (...)")
+# run an INSERT SQL on the Table and emit the result to the TableSink
table_env \
- .sql_update("INSERT INTO RubberOrders SELECT product, amount FROM Orders WHERE product LIKE '%Rubber%'")
+ .execute_sql("INSERT INTO RubberOrders SELECT product, amount FROM Orders WHERE product LIKE '%Rubber%'")
{% endhighlight %}
@@ -126,19 +127,33 @@ CREATE TABLE [catalog_name.][db_name.]table_name
(
{ | }[ , ...n]
[ ]
+ [ ][ , ...n]
)
[COMMENT table_comment]
[PARTITIONED BY (partition_column_name1, partition_column_name2, ...)]
WITH (key1=val1, key2=val2, ...)
-
+ [ LIKE source_table [( )] ]
+
:
- column_name column_type [COMMENT column_comment]
+ column_name column_type [ ] [COMMENT column_comment]
+
+:
+ [CONSTRAINT constraint_name] PRIMARY KEY NOT ENFORCED
+
+:
+ [CONSTRAINT constraint_name] PRIMARY KEY (column_name, ...) NOT ENFORCED
:
column_name AS computed_column_expression [COMMENT column_comment]
:
WATERMARK FOR rowtime_column_name AS watermark_strategy_expression
+
+:
+{
+ { INCLUDING | EXCLUDING } { ALL | CONSTRAINTS | PARTITIONS }
+ | { INCLUDING | EXCLUDING | OVERWRITING } { GENERATED | OPTIONS | WATERMARKS }
+}[, ...]
{% endhighlight %}
@@ -194,6 +209,24 @@ CREATE TABLE Orders (
) WITH ( . . . );
{% endhighlight %}
+**PRIMARY KEY**
+
+Primary key constraint is a hint for Flink to leverage for optimizations. It tells that a column or a set of columns of a table or a view are unique and they **do not** contain null.
+Neither of columns in a primary can be nullable. Primary key therefore uniquely identify a row in a table.
+
+Primary key constraint can be either declared along with a column definition (a column constraint) or as a single line (a table constraint).
+For both cases, it should only be declared as a singleton. If you define multiple primary key constraints at the same time, an exception would be thrown.
+
+##### Validity Check
+
+SQL standard specifies that a constraint can either be `ENFORCED` or `NOT ENFORCED`. This controls if the constraint checks are performed on the incoming/outgoing data.
+Flink does not own the data therefore the only mode we want to support is the `NOT ENFORCED` mode.
+It is up to the user to ensure that the query enforces key integrity.
+
+Flink will assume correctness of the primary key by assuming that the columns nullability is aligned with the columns in primary key. Connectors should ensure those are aligned.
+
+**Notes:** In a CREATE TABLE statement, creating a primary key constraint will alter the columns nullability, that means, a column with primary key constraint is not nullable.
+
**PARTITIONED BY**
Partition the created table by the specified columns. A directory is created for each partition if this table is used as a filesystem sink.
@@ -208,6 +241,101 @@ The key and value of expression `key1=val1` should both be string literal. See d
**Notes:** The table registered with `CREATE TABLE` statement can be used as both table source and table sink, we can not decide if it is used as a source or sink until it is referenced in the DMLs.
+**LIKE clause**
+
+The `LIKE` clause is a variant/combination of SQL features (Feature T171, “LIKE clause in table definition” and Feature T173, “Extended LIKE clause in table definition”). The clause can be used to create a table based on a definition of an existing table. Additionally, users
+can extend the original table or exclude certain parts of it. In contrast to the SQL standard the clause must be defined at the top-level of a CREATE statement. That is because the clause applies to multiple parts of the definition and not only to the schema part.
+
+You can use the clause to reuse (and potentially overwrite) certain connector properties or add watermarks to tables defined externally. For example, you can add a watermark to a table defined in Apache Hive.
+
+Consider the example statement below:
+{% highlight sql %}
+CREATE TABLE Orders (
+ user BIGINT,
+ product STRING,
+ order_time TIMESTAMP(3)
+) WITH (
+ 'connector' = 'kafka',
+ 'scan.startup.mode' = 'earliest-offset'
+);
+
+CREATE TABLE Orders_with_watermark (
+ -- Add watermark definition
+ WATERMARK FOR order_time AS order_time - INTERVAL '5' SECOND
+) WITH (
+ -- Overwrite the startup-mode
+ 'scan.startup.mode' = 'latest-offset'
+)
+LIKE Orders;
+{% endhighlight %}
+
+The resulting table `Orders_with_watermark` will be equivalent to a table created with a following statement:
+{% highlight sql %}
+CREATE TABLE Orders_with_watermark (
+ user BIGINT,
+ product STRING,
+ order_time TIMESTAMP(3),
+ WATERMARK FOR order_time AS order_time - INTERVAL '5' SECOND
+) WITH (
+ 'connector' = 'kafka',
+ 'scan.startup.mode' = 'latest-offset'
+);
+{% endhighlight %}
+
+The merging logic of table features can be controlled with `like options`.
+
+You can control the merging behavior of:
+
+* CONSTRAINTS - constraints such as primary and unique keys
+* GENERATED - computed columns
+* OPTIONS - connector options that describe connector and format properties
+* PARTITIONS - partition of the tables
+* WATERMARKS - watermark declarations
+
+with three different merging strategies:
+
+* INCLUDING - Includes the feature of the source table, fails on duplicate entries, e.g. if an option with the same key exists in both tables.
+* EXCLUDING - Does not include the given feature of the source table.
+* OVERWRITING - Includes the feature of the source table, overwrites duplicate entries of the source table with properties of the new table, e.g. if an option with the same key exists in both tables, the one from the current statement will be used.
+
+Additionally, you can use the `INCLUDING/EXCLUDING ALL` option to specify what should be the strategy if there was no specific strategy defined, i.e. if you use `EXCLUDING ALL INCLUDING WATERMARKS` only the watermarks will be included from the source table.
+
+Example:
+{% highlight sql %}
+-- A source table stored in a filesystem
+CREATE TABLE Orders_in_file (
+ user BIGINT,
+ product STRING,
+ order_time_string STRING,
+ order_time AS to_timestamp(order_time)
+
+)
+PARTITIONED BY user
+WITH (
+ 'connector' = 'filesystem'
+ 'path' = '...'
+);
+
+-- A corresponding table we want to store in kafka
+CREATE TABLE Orders_in_kafka (
+ -- Add watermark definition
+ WATERMARK FOR order_time AS order_time - INTERVAL '5' SECOND
+) WITH (
+ 'connector': 'kafka'
+ ...
+)
+LIKE Orders_in_file (
+ -- Exclude everything besides the computed columns which we need to generate the watermark for.
+ -- We do not want to have the partitions or filesystem options as those do not apply to kafka.
+ EXCLUDING ALL
+ INCLUDING GENERATED
+);
+{% endhighlight %}
+
+If you provide no like options, `INCLUDING ALL OVERWRITING OPTIONS` will be used as a default.
+
+**NOTE** You cannot control the behavior of merging physical fields. Those will be merged as if you applied the `INCLUDING` strategy.
+
{% top %}
## CREATE CATALOG
@@ -249,14 +377,37 @@ The key and value of expression `key1=val1` should both be string literal.
{% top %}
+## CREATE VIEW
+{% highlight sql %}
+CREATE [TEMPORARY] VIEW [IF NOT EXISTS] [catalog_name.][db_name.]view_name
+ [{columnName [, columnName ]* }] [COMMENT view_comment]
+ AS query_expression
+{% endhighlight %}
+
+Create a view with the given query expression. If a view with the same name already exists in the catalog, an exception is thrown.
+
+**TEMPORARY**
+
+Create temporary view that has catalog and database namespaces and overrides views.
+
+**IF NOT EXISTS**
+
+If the view already exists, nothing happens.
+
+{% top %}
+
## CREATE FUNCTION
{% highlight sql%}
CREATE [TEMPORARY|TEMPORARY SYSTEM] FUNCTION
[IF NOT EXISTS] [catalog_name.][db_name.]function_name
- AS identifier [LANGUAGE JAVA|SCALA]
+ AS identifier [LANGUAGE JAVA|SCALA|PYTHON]
{% endhighlight %}
-Create a catalog function that has catalog and database namespaces with the identifier which is full classpath for JAVA/SCALA and optional language tag. If a function with the same name already exists in the catalog, an exception is thrown.
+Create a catalog function that has catalog and database namespaces with the identifier and optional language tag. If a function with the same name already exists in the catalog, an exception is thrown.
+
+If the language tag is JAVA/SCALA, the identifier is the full classpath of the UDF. For the implementation of Java/Scala UDF, please refer to [User-defined Functions]({{ site.baseurl }}/dev/table/functions/udfs.html) for more details.
+
+If the language tag is PYTHON, the identifier is the fully qualified name of the UDF, e.g. `pyflink.table.tests.test_udf.add`. For the implementation of Python UDF, please refer to [Python UDFs]({{ site.baseurl }}/dev/table/python/python_udfs.html) for more details.
**TEMPORARY**
@@ -270,6 +421,6 @@ Create temporary system function that has no namespace and overrides built-in fu
If the function already exists, nothing happens.
-**LANGUAGE JAVA\|SCALA**
+**LANGUAGE JAVA\|SCALA\|PYTHON**
-Language tag to instruct Flink runtime how to execute the function. Currently only JAVA and SCALA are supported, the default language for a function is JAVA.
+Language tag to instruct Flink runtime how to execute the function. Currently only JAVA, SCALA and PYTHON are supported, the default language for a function is JAVA.
diff --git a/docs/dev/table/sql/create.zh.md b/docs/dev/table/sql/create.zh.md
index 5c6099ae82ba53..027debfe09ec1c 100644
--- a/docs/dev/table/sql/create.zh.md
+++ b/docs/dev/table/sql/create.zh.md
@@ -31,11 +31,12 @@ CREATE 语句用于向当前或指定的 [Catalog]({{ site.baseurl }}/zh/dev/tab
- CREATE TABLE
- CREATE DATABASE
+- CREATE VIEW
- CREATE FUNCTION
## 执行 CREATE 语句
-可以使用 `TableEnvironment` 中的 `sqlUpdate()` 方法执行 CREATE 语句,也可以在 [SQL CLI]({{ site.baseurl }}/zh/dev/table/sqlClient.html) 中执行 CREATE 语句。 若 CREATE 操作执行成功,`sqlUpdate()` 方法不返回任何内容,否则会抛出异常。
+可以使用 `TableEnvironment` 中的 `executeSql()` 方法执行 CREATE 语句,也可以在 [SQL CLI]({{ site.baseurl }}/zh/dev/table/sqlClient.html) 中执行 CREATE 语句。 若 CREATE 操作执行成功,`executeSql()` 方法返回 'OK',否则会抛出异常。
以下的例子展示了如何在 `TableEnvironment` 和 SQL CLI 中执行一个 CREATE 语句。
@@ -45,18 +46,18 @@ CREATE 语句用于向当前或指定的 [Catalog]({{ site.baseurl }}/zh/dev/tab
EnvironmentSettings settings = EnvironmentSettings.newInstance()...
TableEnvironment tableEnv = TableEnvironment.create(settings);
-// 对已经已经注册的表进行 SQL 查询
+// 对已注册的表进行 SQL 查询
// 注册名为 “Orders” 的表
-tableEnv.sqlUpdate("CREATE TABLE Orders (`user` BIGINT, product STRING, amount INT) WITH (...)");
+tableEnv.executeSql("CREATE TABLE Orders (`user` BIGINT, product STRING, amount INT) WITH (...)");
// 在表上执行 SQL 查询,并把得到的结果作为一个新的表
Table result = tableEnv.sqlQuery(
"SELECT product, amount FROM Orders WHERE product LIKE '%Rubber%'");
-// SQL 对已注册的表进行 update 操作
+// 对已注册的表进行 INSERT 操作
// 注册 TableSink
-tableEnv.sqlUpdate("CREATE TABLE RubberOrders(product STRING, amount INT) WITH (...)");
-// 在表上执行 SQL 更新查询并向 TableSink 发出结果
-tableEnv.sqlUpdate(
+tableEnv.executeSql("CREATE TABLE RubberOrders(product STRING, amount INT) WITH (...)");
+// 在表上执行 INSERT 语句并向 TableSink 发出结果
+tableEnv.executeSql(
"INSERT INTO RubberOrders SELECT product, amount FROM Orders WHERE product LIKE '%Rubber%'");
{% endhighlight %}
@@ -66,40 +67,40 @@ tableEnv.sqlUpdate(
val settings = EnvironmentSettings.newInstance()...
val tableEnv = TableEnvironment.create(settings)
-// 对已经已经注册的表进行 SQL 查询
+// 对已注册的表进行 SQL 查询
// 注册名为 “Orders” 的表
-tableEnv.sqlUpdate("CREATE TABLE Orders (`user` BIGINT, product STRING, amount INT) WITH (...)");
+tableEnv.executeSql("CREATE TABLE Orders (`user` BIGINT, product STRING, amount INT) WITH (...)");
// 在表上执行 SQL 查询,并把得到的结果作为一个新的表
val result = tableEnv.sqlQuery(
"SELECT product, amount FROM Orders WHERE product LIKE '%Rubber%'");
-// SQL 对已注册的表进行 update 操作
+// 对已注册的表进行 INSERT 操作
// 注册 TableSink
-tableEnv.sqlUpdate("CREATE TABLE RubberOrders(product STRING, amount INT) WITH ('connector.path'='/path/to/file' ...)");
-// 在表上执行 SQL 更新查询并向 TableSink 发出结果
-tableEnv.sqlUpdate(
+tableEnv.executeSql("CREATE TABLE RubberOrders(product STRING, amount INT) WITH ('connector.path'='/path/to/file' ...)");
+// 在表上执行 INSERT 语句并向 TableSink 发出结果
+tableEnv.executeSql(
"INSERT INTO RubberOrders SELECT product, amount FROM Orders WHERE product LIKE '%Rubber%'")
{% endhighlight %}
{% highlight python %}
-settings = EnvironmentSettings.newInstance()...
-table_env = TableEnvironment.create(settings)
+settings = EnvironmentSettings.new_instance()...
+table_env = StreamTableEnvironment.create(env, settings)
-# 对已经已经注册的表进行 SQL 查询
+# 对已经注册的表进行 SQL 查询
# 注册名为 “Orders” 的表
-tableEnv.sqlUpdate("CREATE TABLE Orders (`user` BIGINT, product STRING, amount INT) WITH (...)");
+table_env.execute_sql("CREATE TABLE Orders (`user` BIGINT, product STRING, amount INT) WITH (...)");
# 在表上执行 SQL 查询,并把得到的结果作为一个新的表
-result = tableEnv.sqlQuery(
+result = table_env.sql_query(
"SELECT product, amount FROM Orders WHERE product LIKE '%Rubber%'");
-# SQL 对已注册的表进行 update 操作
+# 对已注册的表进行 INSERT 操作
# 注册 TableSink
-table_env.sql_update("CREATE TABLE RubberOrders(product STRING, amount INT) WITH (...)")
-# 在表上执行 SQL 更新查询并向 TableSink 发出结果
+table_env.execute_sql("CREATE TABLE RubberOrders(product STRING, amount INT) WITH (...)")
+# 在表上执行 INSERT 语句并向 TableSink 发出结果
table_env \
- .sql_update("INSERT INTO RubberOrders SELECT product, amount FROM Orders WHERE product LIKE '%Rubber%'")
+ .execute_sql("INSERT INTO RubberOrders SELECT product, amount FROM Orders WHERE product LIKE '%Rubber%'")
{% endhighlight %}
@@ -126,13 +127,21 @@ CREATE TABLE [catalog_name.][db_name.]table_name
(
{ | }[ , ...n]
[ ]
+ [ ][ , ...n]
)
[COMMENT table_comment]
[PARTITIONED BY (partition_column_name1, partition_column_name2, ...)]
WITH (key1=val1, key2=val2, ...)
+ [ LIKE source_table [( )] ]
:
- column_name column_type [COMMENT column_comment]
+ column_name column_type [ ] [COMMENT column_comment]
+
+:
+ [CONSTRAINT constraint_name] PRIMARY KEY NOT ENFORCED
+
+:
+ [CONSTRAINT constraint_name] PRIMARY KEY (column_name, ...) NOT ENFORCED
:
column_name AS computed_column_expression [COMMENT column_comment]
@@ -140,6 +149,12 @@ CREATE TABLE [catalog_name.][db_name.]table_name
:
WATERMARK FOR rowtime_column_name AS watermark_strategy_expression
+:
+{
+ { INCLUDING | EXCLUDING } { ALL | CONSTRAINTS | PARTITIONS }
+ | { INCLUDING | EXCLUDING | OVERWRITING } { GENERATED | OPTIONS | WATERMARKS }
+}[, ...]
+
{% endhighlight %}
根据指定的表名创建一个表,如果同名表已经在 catalog 中存在了,则无法注册。
@@ -194,6 +209,22 @@ CREATE TABLE Orders (
) WITH ( . . . );
{% endhighlight %}
+
+**PRIMARY KEY**
+
+主键用作 Flink 优化的一种提示信息。主键限制表明一张表或视图的某个(些)列是唯一的并且不包含 Null 值。
+主键声明的列都是非 nullable 的。因此主键可以被用作表行级别的唯一标识。
+
+主键可以和列的定义一起声明,也可以独立声明为表的限制属性,不管是哪种方式,主键都不可以重复定义,否则 Flink 会报错。
+
+##### 有效性检查
+
+SQL 标准主键限制可以有两种模式:`ENFORCED` 或者 `NOT ENFORCED`。 它申明了是否输入/出数据会做合法性检查(是否唯一)。Flink 不存储数据因此只支持 `NOT ENFORCED` 模式,即不做检查,用户需要自己保证唯一性。
+
+Flink 假设声明了主键的列都是不包含 Null 值的,Connector 在处理数据时需要自己保证语义正确。
+
+**Notes:** 在 CREATE TABLE 语句中,创建主键会修改列的 nullable 属性,主键声明的列默认都是非 Nullable 的。
+
**PARTITIONED BY**
根据指定的列对已经创建的表进行分区。若表使用 filesystem sink ,则将会为每个分区创建一个目录。
@@ -208,6 +239,103 @@ CREATE TABLE Orders (
**注意:** 使用 `CREATE TABLE` 语句注册的表均可用作 table source 和 table sink。 在被 DML 语句引用前,我们无法决定其实际用于 source 抑或是 sink。
+**LIKE**
+
+`LIKE` 子句来源于两种 SQL 特性的变体/组合(Feature T171,“表定义中的 LIKE 语法” 和 Feature T173,“表定义中的 LIKE 语法扩展”)。LIKE 子句可以基于现有表的定义去创建新表,并且可以扩展或排除原始表中的某些部分。与 SQL 标准相反,LIKE 子句必须在 CREATE 语句中定义,并且是基于 CREATE 语句的更上层定义,这是因为 LIKE 子句可以用于定义表的多个部分,而不仅仅是 schema 部分。
+
+你可以使用该子句,重用(或改写)指定的连接器配置属性或者可以向外部表添加 watermark 定义,例如可以向 Apache Hive 中定义的表添加 watermark 定义。
+
+示例如下:
+
+{% highlight sql %}
+CREATE TABLE Orders (
+ user BIGINT,
+ product STRING,
+ order_time TIMESTAMP(3)
+) WITH (
+ 'connector' = 'kafka',
+ 'scan.startup.mode' = 'earliest-offset'
+);
+
+CREATE TABLE Orders_with_watermark (
+ -- 添加 watermark 定义
+ WATERMARK FOR order_time AS order_time - INTERVAL '5' SECOND
+) WITH (
+ -- 改写 startup-mode 属性
+ 'scan.startup.mode' = 'latest-offset'
+)
+LIKE Orders;
+{% endhighlight %}
+
+结果表 `Orders_with_watermark` 等效于使用以下语句创建的表:
+
+{% highlight sql %}
+CREATE TABLE Orders_with_watermark (
+ user BIGINT,
+ product STRING,
+ order_time TIMESTAMP(3),
+ WATERMARK FOR order_time AS order_time - INTERVAL '5' SECOND
+) WITH (
+ 'connector' = 'kafka',
+ 'scan.startup.mode' = 'latest-offset'
+);
+{% endhighlight %}
+
+表属性的合并逻辑可以用 `like options` 来控制。
+
+可以控制合并的表属性如下:
+
+* CONSTRAINTS - 主键和唯一键约束
+* GENERATED - 计算列
+* OPTIONS - 连接器信息、格式化方式等配置项
+* PARTITIONS - 表分区信息
+* WATERMARKS - watermark 定义
+
+并且有三种不同的表属性合并策略:
+
+* INCLUDING - 新表包含源表(source table)所有的表属性,如果和源表的表属性重复则会直接失败,例如新表和源表存在相同 key 的属性。
+* EXCLUDING - 新表不包含源表指定的任何表属性。
+* OVERWRITING - 新表包含源表的表属性,但如果出现重复项,则会用新表的表属性覆盖源表中的重复表属性,例如,两个表中都存在相同 key 的属性,则会使用当前语句中定义的 key 的属性值。
+
+并且你可以使用 `INCLUDING/EXCLUDING ALL` 这种声明方式来指定使用怎样的合并策略,例如使用 `EXCLUDING ALL INCLUDING WATERMARKS`,那么代表只有源表的 WATERMARKS 属性才会被包含进新表。
+
+示例如下:
+{% highlight sql %}
+
+-- 存储在文件系统的源表
+CREATE TABLE Orders_in_file (
+ user BIGINT,
+ product STRING,
+ order_time_string STRING,
+ order_time AS to_timestamp(order_time)
+
+)
+PARTITIONED BY user
+WITH (
+ 'connector' = 'filesystem'
+ 'path' = '...'
+);
+
+-- 对应存储在 kafka 的源表
+CREATE TABLE Orders_in_kafka (
+ -- 添加 watermark 定义
+ WATERMARK FOR order_time AS order_time - INTERVAL '5' SECOND
+) WITH (
+ 'connector': 'kafka'
+ ...
+)
+LIKE Orders_in_file (
+ -- 排除需要生成 watermark 的计算列之外的所有内容。
+ -- 去除不适用于 kafka 的所有分区和文件系统的相关属性。
+ EXCLUDING ALL
+ INCLUDING GENERATED
+);
+{% endhighlight %}
+
+如果未提供 like 配置项(like options),默认将使用 `INCLUDING ALL OVERWRITING OPTIONS` 的合并策略。
+
+**注意:** 您无法选择物理列的合并策略,当物理列进行合并时就如使用了 `INCLUDING` 策略。
+
{% top %}
## CREATE CATALOG
@@ -249,14 +377,37 @@ CREATE DATABASE [IF NOT EXISTS] [catalog_name.]db_name
{% top %}
+## CREATE VIEW
+{% highlight sql %}
+CREATE [TEMPORARY] VIEW [IF NOT EXISTS] [catalog_name.][db_name.]view_name
+ [{columnName [, columnName ]* }] [COMMENT view_comment]
+ AS query_expression
+{% endhighlight %}
+
+根据给定的 query 语句创建一个视图。若数据库中已经存在同名视图会抛出异常.
+
+**TEMPORARY**
+
+创建一个有 catalog 和数据库命名空间的临时视图,并覆盖原有的视图。
+
+**IF NOT EXISTS**
+
+若该视图已经存在,则不会进行任何操作。
+
+{% top %}
+
## CREATE FUNCTION
{% highlight sql%}
CREATE [TEMPORARY|TEMPORARY SYSTEM] FUNCTION
[IF NOT EXISTS] [[catalog_name.]db_name.]function_name
- AS identifier [LANGUAGE JAVA|SCALA]
+ AS identifier [LANGUAGE JAVA|SCALA|PYTHON]
{% endhighlight %}
-创建一个有 catalog 和数据库命名空间的 catalog function ,其需要指定 JAVA / SCALA 或其他 language tag 完整的 classpath。 若 catalog 中,已经有同名的函数注册了,则无法注册。
+创建一个有 catalog 和数据库命名空间的 catalog function ,需要指定一个 identifier ,可指定 language tag 。 若 catalog 中,已经有同名的函数注册了,则无法注册。
+
+如果 language tag 是 JAVA 或者 SCALA ,则 identifier 是 UDF 实现类的全限定名。关于 JAVA/SCALA UDF 的实现,请参考 [自定义函数]({{ site.baseurl }}/zh/dev/table/functions/udfs.html)。
+
+如果 language tag 是 PYTHON ,则 identifier 是 UDF 对象的全限定名,例如 `pyflink.table.tests.test_udf.add`。关于 PYTHON UDF 的实现,请参考 [Python UDFs]({{ site.baseurl }}/zh/dev/table/python/python_udfs.html)。
**TEMPORARY**
@@ -270,7 +421,7 @@ CREATE [TEMPORARY|TEMPORARY SYSTEM] FUNCTION
若该函数已经存在,则不会进行任何操作。
-**LANGUAGE JAVA\|SCALA**
+**LANGUAGE JAVA\|SCALA\|PYTHON**
-Language tag 用于指定 Flink runtime 如何执行这个函数。目前,只支持 JAVA 和 SCALA,且函数的默认语言为 JAVA。
+Language tag 用于指定 Flink runtime 如何执行这个函数。目前,只支持 JAVA, SCALA 和 PYTHON,且函数的默认语言为 JAVA。
diff --git a/docs/dev/table/sql/describe.md b/docs/dev/table/sql/describe.md
new file mode 100644
index 00000000000000..de188692ec9d49
--- /dev/null
+++ b/docs/dev/table/sql/describe.md
@@ -0,0 +1,202 @@
+---
+title: "DESCRIBE Statements"
+nav-parent_id: sql
+nav-pos: 7
+---
+
+
+* This will be replaced by the TOC
+{:toc}
+
+DESCRIBE statements are used to describe the schema of a table or a view.
+
+
+## Run a DESCRIBE statement
+
+DESCRIBE statements can be executed with the `executeSql()` method of the `TableEnvironment`, or executed in [SQL CLI]({{ site.baseurl }}/dev/table/sqlClient.html). The `executeSql()` method returns the schema of given table for a successful DESCRIBE operation, otherwise will throw an exception.
+
+The following examples show how to run a DESCRIBE statement in `TableEnvironment` and in SQL CLI.
+
+
+
+{% highlight java %}
+EnvironmentSettings settings = EnvironmentSettings.newInstance()...
+TableEnvironment tableEnv = TableEnvironment.create(settings);
+
+// register a table named "Orders"
+tableEnv.executeSql(
+ "CREATE TABLE Orders (" +
+ " `user` BIGINT NOT NULl," +
+ " product VARCHAR(32)," +
+ " amount INT," +
+ " ts TIMESTAMP(3)," +
+ " ptime AS PROCTIME()," +
+ " PRIMARY KEY(`user`) NOT ENFORCED," +
+ " WATERMARK FOR ts AS ts - INTERVAL '1' SECONDS" +
+ ") with (...)");
+
+// print the schema
+tableEnv.executeSql("DESCRIBE Orders").print();
+
+{% endhighlight %}
+
+
+
+{% highlight scala %}
+val settings = EnvironmentSettings.newInstance()...
+val tableEnv = TableEnvironment.create(settings)
+
+// register a table named "Orders"
+ tableEnv.executeSql(
+ "CREATE TABLE Orders (" +
+ " `user` BIGINT NOT NULl," +
+ " product VARCHAR(32)," +
+ " amount INT," +
+ " ts TIMESTAMP(3)," +
+ " ptime AS PROCTIME()," +
+ " PRIMARY KEY(`user`) NOT ENFORCED," +
+ " WATERMARK FOR ts AS ts - INTERVAL '1' SECONDS" +
+ ") with (...)")
+
+// print the schema
+tableEnv.executeSql("DESCRIBE Orders").print()
+
+{% endhighlight %}
+
+
+
+{% highlight python %}
+settings = EnvironmentSettings.new_instance()...
+table_env = StreamTableEnvironment.create(env, settings)
+
+# register a table named "Orders"
+table_env.execute_sql( \
+ "CREATE TABLE Orders ("
+ " `user` BIGINT NOT NULl,"
+ " product VARCHAR(32),"
+ " amount INT,"
+ " ts TIMESTAMP(3),"
+ " ptime AS PROCTIME(),"
+ " PRIMARY KEY(`user`) NOT ENFORCED,"
+ " WATERMARK FOR ts AS ts - INTERVAL '1' SECONDS"
+ ") with (...)");
+
+# print the schema
+table_env.execute_sql("DESCRIBE Orders").print()
+
+{% endhighlight %}
+
+
+
+{% highlight sql %}
+Flink SQL> CREATE TABLE Orders (
+> `user` BIGINT NOT NULl,
+> product VARCHAR(32),
+> amount INT,
+> ts TIMESTAMP(3),
+> ptime AS PROCTIME(),
+> PRIMARY KEY(`user`) NOT ENFORCED,
+> WATERMARK FOR ts AS ts - INTERVAL '1' SECONDS
+> ) with (
+> ...
+> );
+[INFO] Table has been created.
+
+Flink SQL> DESCRIBE Orders;
+
+{% endhighlight %}
+
+{% highlight text %}
+
+root
+ |-- user: BIGINT NOT NULL
+ |-- product: VARCHAR(32)
+ |-- amount: INT
+ |-- ts: TIMESTAMP(3) *ROWTIME*
+ |-- ptime: TIMESTAMP(3) NOT NULL *PROCTIME* AS PROCTIME()
+ |-- WATERMARK FOR ts AS `ts` - INTERVAL '1' SECOND
+ |-- CONSTRAINT PK_3599338 PRIMARY KEY (user)
+
+{% endhighlight %}
+
+
+
+
+{% top %}
+
+## 语法
+
+{% highlight sql %}
+DESCRIBE [catalog_name.][db_name.]table_name
+{% endhighlight %}
diff --git a/docs/dev/table/sql/drop.md b/docs/dev/table/sql/drop.md
index 6f340e8bd446af..c12105ff338bac 100644
--- a/docs/dev/table/sql/drop.md
+++ b/docs/dev/table/sql/drop.md
@@ -31,11 +31,12 @@ Flink SQL supports the following DROP statements for now:
- DROP TABLE
- DROP DATABASE
+- DROP VIEW
- DROP FUNCTION
## Run a DROP statement
-DROP statements can be executed with the `sqlUpdate()` method of the `TableEnvironment`, or executed in [SQL CLI]({{ site.baseurl }}/dev/table/sqlClient.html). The `sqlUpdate()` method returns nothing for a successful DROP operation, otherwise will throw an exception.
+DROP statements can be executed with the `executeSql()` method of the `TableEnvironment`, or executed in [SQL CLI]({{ site.baseurl }}/dev/table/sqlClient.html). The `executeSql()` method returns 'OK' for a successful DROP operation, otherwise will throw an exception.
The following examples show how to run a DROP statement in `TableEnvironment` and in SQL CLI.
@@ -46,16 +47,18 @@ EnvironmentSettings settings = EnvironmentSettings.newInstance()...
TableEnvironment tableEnv = TableEnvironment.create(settings);
// register a table named "Orders"
-tableEnv.sqlUpdate("CREATE TABLE Orders (`user` BIGINT, product STRING, amount INT) WITH (...)");
+tableEnv.executeSql("CREATE TABLE Orders (`user` BIGINT, product STRING, amount INT) WITH (...)");
// a string array: ["Orders"]
-String[] tables = tableEnv.listTable();
+String[] tables = tableEnv.listTables();
+// or tableEnv.executeSql("SHOW TABLES").print();
// drop "Orders" table from catalog
-tableEnv.sqlUpdate("DROP TABLE Orders");
+tableEnv.executeSql("DROP TABLE Orders");
// an empty string array
-String[] tables = tableEnv.listTable();
+String[] tables = tableEnv.listTables();
+// or tableEnv.executeSql("SHOW TABLES").print();
{% endhighlight %}
@@ -65,32 +68,36 @@ val settings = EnvironmentSettings.newInstance()...
val tableEnv = TableEnvironment.create(settings)
// register a table named "Orders"
-tableEnv.sqlUpdate("CREATE TABLE Orders (`user` BIGINT, product STRING, amount INT) WITH (...)");
+tableEnv.executeSql("CREATE TABLE Orders (`user` BIGINT, product STRING, amount INT) WITH (...)")
// a string array: ["Orders"]
-val tables = tableEnv.listTable()
+val tables = tableEnv.listTables()
+// or tableEnv.executeSql("SHOW TABLES").print()
// drop "Orders" table from catalog
-tableEnv.sqlUpdate("DROP TABLE Orders")
+tableEnv.executeSql("DROP TABLE Orders")
// an empty string array
-val tables = tableEnv.listTable()
+val tables = tableEnv.listTables()
+// or tableEnv.executeSql("SHOW TABLES").print()
{% endhighlight %}
@@ -143,6 +150,25 @@ Dropping a non-empty database triggers an exception. Enabled by default.
Dropping a non-empty database also drops all associated tables and functions.
+## DROP VIEW
+
+{% highlight sql %}
+DROP [TEMPORARY] VIEW [IF EXISTS] [catalog_name.][db_name.]view_name
+{% endhighlight %}
+
+Drop a view that has catalog and database namespaces. If the view to drop does not exist, an exception is thrown.
+
+**TEMPORARY**
+
+Drop temporary view that has catalog and database namespaces.
+
+**IF EXISTS**
+
+If the view does not exist, nothing happens.
+
+**MAINTAIN DEPENDENCIES**
+Flink does not maintain dependencies of view by CASCADE/RESTRICT keywords, the current way is producing postpone error message when user tries to use the view under the scenarios like the underlying table of view has been dropped.
+
## DROP FUNCTION
{% highlight sql%}
diff --git a/docs/dev/table/sql/drop.zh.md b/docs/dev/table/sql/drop.zh.md
index 51ac456c7ad681..911ae97c20d7a9 100644
--- a/docs/dev/table/sql/drop.zh.md
+++ b/docs/dev/table/sql/drop.zh.md
@@ -31,11 +31,12 @@ Flink SQL 目前支持以下 DROP 语句:
- DROP TABLE
- DROP DATABASE
+- DROP VIEW
- DROP FUNCTION
## 执行 DROP 语句
-可以使用 `TableEnvironment` 中的 `sqlUpdate()` 方法执行 DROP 语句,也可以在 [SQL CLI]({{ site.baseurl }}/zh/dev/table/sqlClient.html) 中执行 DROP 语句。 若 DROP 操作执行成功,`sqlUpdate()` 方法不返回任何内容,否则会抛出异常。
+可以使用 `TableEnvironment` 中的 `executeSql()` 方法执行 DROP 语句,也可以在 [SQL CLI]({{ site.baseurl }}/zh/dev/table/sqlClient.html) 中执行 DROP 语句。 若 DROP 操作执行成功,`executeSql()` 方法返回 'OK',否则会抛出异常。
以下的例子展示了如何在 `TableEnvironment` 和 SQL CLI 中执行一个 DROP 语句。
@@ -46,16 +47,18 @@ EnvironmentSettings settings = EnvironmentSettings.newInstance()...
TableEnvironment tableEnv = TableEnvironment.create(settings);
// 注册名为 “Orders” 的表
-tableEnv.sqlUpdate("CREATE TABLE Orders (`user` BIGINT, product STRING, amount INT) WITH (...)");
+tableEnv.executeSql("CREATE TABLE Orders (`user` BIGINT, product STRING, amount INT) WITH (...)");
// 字符串数组: ["Orders"]
-String[] tables = tableEnv.listTable();
+String[] tables = tableEnv.listTables();
+// or tableEnv.executeSql("SHOW TABLES").print();
// 从 catalog 删除 “Orders” 表
-tableEnv.sqlUpdate("DROP TABLE Orders");
+tableEnv.executeSql("DROP TABLE Orders");
// 空字符串数组
-String[] tables = tableEnv.listTable();
+String[] tables = tableEnv.listTables();
+// or tableEnv.executeSql("SHOW TABLES").print();
{% endhighlight %}
@@ -143,6 +150,25 @@ DROP DATABASE [IF EXISTS] [catalog_name.]db_name [ (RESTRICT | CASCADE) ]
删除一个非空数据库时,把相关联的表与函数一并删除。
+## DROP VIEW
+
+{% highlight sql %}
+DROP [TEMPORARY] VIEW [IF EXISTS] [catalog_name.][db_name.]view_name
+{% endhighlight %}
+
+删除一个有 catalog 和数据库命名空间的视图。若需要删除的视图不存在,则会产生异常。
+
+**TEMPORARY**
+
+删除一个有 catalog 和数据库命名空间的临时视图。
+
+**IF EXISTS**
+
+若视图不存在,则不会进行任何操作。
+
+**依赖管理**
+Flink 没有使用 CASCADE / RESTRICT 关键字来维护视图的依赖关系,当前的方案是在用户使用视图时再提示错误信息,比如在视图的底层表已经被删除等场景。
+
## DROP FUNCTION
{% highlight sql%}
diff --git a/docs/dev/table/sql/explain.md b/docs/dev/table/sql/explain.md
new file mode 100644
index 00000000000000..ae26b0652aa300
--- /dev/null
+++ b/docs/dev/table/sql/explain.md
@@ -0,0 +1,180 @@
+---
+title: "EXPLAIN Statements"
+nav-parent_id: sql
+nav-pos: 8
+---
+
+
+* This will be replaced by the TOC
+{:toc}
+
+EXPLAIN statements are used to explain the logical and optimized query plans of a query or an INSERT statement.
+
+
+## Run an EXPLAIN statement
+
+EXPLAIN statements can be executed with the `executeSql()` method of the `TableEnvironment`, or executed in [SQL CLI]({{ site.baseurl }}/dev/table/sqlClient.html). The `executeSql()` method returns explain result for a successful EXPLAIN operation, otherwise will throw an exception.
+
+The following examples show how to run an EXPLAIN statement in `TableEnvironment` and in SQL CLI.
+
+
+
+{% highlight java %}
+StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
+StreamTableEnvironment tEnv = StreamTableEnvironment.create(env);
+
+// register a table named "Orders"
+tEnv.executeSql("CREATE TABLE MyTable1 (count bigint, work VARCHAR(256) WITH (...)");
+tEnv.executeSql("CREATE TABLE MyTable2 (count bigint, work VARCHAR(256) WITH (...)");
+
+// explain SELECT statement through TableEnvironment.explainSql()
+String explanation = tEnv.explainSql(
+ "SELECT count, word FROM MyTable1 WHERE word LIKE 'F%' " +
+ "UNION ALL " +
+ "SELECT count, word FROM MyTable2");
+System.out.println(explanation);
+
+// explain SELECT statement through TableEnvironment.executeSql()
+TableResult tableResult = tEnv.executeSql(
+ "EXPLAIN PLAN FOR " +
+ "SELECT count, word FROM MyTable1 WHERE word LIKE 'F%' " +
+ "UNION ALL " +
+ "SELECT count, word FROM MyTable2");
+tableResult.print();
+
+{% endhighlight %}
+
+
+
+{% highlight scala %}
+val env = StreamExecutionEnvironment.getExecutionEnvironment()
+val tEnv = StreamTableEnvironment.create(env)
+
+// register a table named "Orders"
+tEnv.executeSql("CREATE TABLE MyTable1 (count bigint, work VARCHAR(256) WITH (...)")
+tEnv.executeSql("CREATE TABLE MyTable2 (count bigint, work VARCHAR(256) WITH (...)")
+
+// explain SELECT statement through TableEnvironment.explainSql()
+val explanation = tEnv.explainSql(
+ "SELECT count, word FROM MyTable1 WHERE word LIKE 'F%' " +
+ "UNION ALL " +
+ "SELECT count, word FROM MyTable2")
+println(explanation)
+
+// explain SELECT statement through TableEnvironment.executeSql()
+val tableResult = tEnv.executeSql(
+ "EXPLAIN PLAN FOR " +
+ "SELECT count, word FROM MyTable1 WHERE word LIKE 'F%' " +
+ "UNION ALL " +
+ "SELECT count, word FROM MyTable2")
+tableResult.print()
+
+{% endhighlight %}
+
+
+
+{% highlight python %}
+settings = EnvironmentSettings.new_instance()...
+table_env = StreamTableEnvironment.create(env, settings)
+
+t_env.execute_sql("CREATE TABLE MyTable1 (count bigint, work VARCHAR(256) WITH (...)")
+t_env.execute_sql("CREATE TABLE MyTable2 (count bigint, work VARCHAR(256) WITH (...)")
+
+# explain SELECT statement through TableEnvironment.explain_sql()
+explanation1 = t_env.explain_sql(
+ "SELECT count, word FROM MyTable1 WHERE word LIKE 'F%' "
+ "UNION ALL "
+ "SELECT count, word FROM MyTable2")
+print(explanation1)
+
+# explain SELECT statement through TableEnvironment.execute_sql()
+table_result = t_env.execute_sql(
+ "EXPLAIN PLAN FOR "
+ "SELECT count, word FROM MyTable1 WHERE word LIKE 'F%' "
+ "UNION ALL "
+ "SELECT count, word FROM MyTable2")
+table_result.print()
+
+{% endhighlight %}
+
+
+
+{% highlight sql %}
+Flink SQL> CREATE TABLE MyTable1 (count bigint, work VARCHAR(256);
+[INFO] Table has been created.
+
+Flink SQL> CREATE TABLE MyTable2 (count bigint, work VARCHAR(256);
+[INFO] Table has been created.
+
+Flink SQL> EXPLAIN PLAN FOR SELECT count, word FROM MyTable1 WHERE word LIKE 'F%'
+> UNION ALL
+> SELECT count, word FROM MyTable2;
+
+{% endhighlight %}
+
+
+{% top %}
+
+## Syntax
+
+{% highlight sql %}
+EXPLAIN PLAN FOR
+{% endhighlight %}
+
+For query syntax, please refer to [Queries]({{ site.baseurl }}/dev/table/sql/queries.html#supported-syntax) page.
+For INSERT, please refer to [INSERT]({{ site.baseurl }}/dev/table/sql/insert.html) page.
diff --git a/docs/dev/table/sql/explain.zh.md b/docs/dev/table/sql/explain.zh.md
new file mode 100644
index 00000000000000..e2f2c1e9d370ef
--- /dev/null
+++ b/docs/dev/table/sql/explain.zh.md
@@ -0,0 +1,182 @@
+---
+title: "EXPLAIN 语句"
+nav-parent_id: sql
+nav-pos: 8
+---
+
+
+* This will be replaced by the TOC
+{:toc}
+
+EXPLAIN 语句用来解释一条 query 语句或者 INSERT 语句的逻辑计划和优化后的计划。
+
+
+## 运行一条 EXPLAIN 语句
+
+EXPLAIN 语句可以通过 `TableEnvironment` 的 `executeSql()` 执行,也可以在 [SQL CLI]({{ site.baseurl }}/zh/dev/table/sqlClient.html) 中执行 EXPLAIN 语句。 若 EXPLAIN 操作执行成功,`executeSql()` 方法返回解释的结果,否则会抛出异常。
+
+以下的例子展示了如何在 TableEnvironment 和 SQL CLI 中执行一条 EXPLAIN 语句。
+
+
+
+{% highlight java %}
+StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
+StreamTableEnvironment tEnv = StreamTableEnvironment.create(env);
+
+// register a table named "Orders"
+tEnv.executeSql("CREATE TABLE MyTable1 (count bigint, work VARCHAR(256) WITH (...)");
+tEnv.executeSql("CREATE TABLE MyTable2 (count bigint, work VARCHAR(256) WITH (...)");
+
+// explain SELECT statement through TableEnvironment.explainSql()
+String explanation = tEnv.explainSql(
+ "SELECT count, word FROM MyTable1 WHERE word LIKE 'F%' " +
+ "UNION ALL " +
+ "SELECT count, word FROM MyTable2");
+System.out.println(explanation);
+
+// explain SELECT statement through TableEnvironment.executeSql()
+TableResult tableResult = tEnv.executeSql(
+ "EXPLAIN PLAN FOR " +
+ "SELECT count, word FROM MyTable1 WHERE word LIKE 'F%' " +
+ "UNION ALL " +
+ "SELECT count, word FROM MyTable2");
+tableResult.print();
+
+{% endhighlight %}
+
+
+
+{% highlight scala %}
+val env = StreamExecutionEnvironment.getExecutionEnvironment()
+val tEnv = StreamTableEnvironment.create(env)
+
+// register a table named "Orders"
+tEnv.executeSql("CREATE TABLE MyTable1 (count bigint, work VARCHAR(256) WITH (...)")
+tEnv.executeSql("CREATE TABLE MyTable2 (count bigint, work VARCHAR(256) WITH (...)")
+
+// explain SELECT statement through TableEnvironment.explainSql()
+val explanation = tEnv.explainSql(
+ "SELECT count, word FROM MyTable1 WHERE word LIKE 'F%' " +
+ "UNION ALL " +
+ "SELECT count, word FROM MyTable2")
+println(explanation)
+
+// explain SELECT statement through TableEnvironment.executeSql()
+val tableResult = tEnv.executeSql(
+ "EXPLAIN PLAN FOR " +
+ "SELECT count, word FROM MyTable1 WHERE word LIKE 'F%' " +
+ "UNION ALL " +
+ "SELECT count, word FROM MyTable2")
+tableResult.print()
+
+{% endhighlight %}
+
+
+
+{% highlight python %}
+settings = EnvironmentSettings.new_instance()...
+table_env = StreamTableEnvironment.create(env, settings)
+
+t_env.execute_sql("CREATE TABLE MyTable1 (count bigint, work VARCHAR(256) WITH (...)")
+t_env.execute_sql("CREATE TABLE MyTable2 (count bigint, work VARCHAR(256) WITH (...)")
+
+# explain SELECT statement through TableEnvironment.explain_sql()
+explanation1 = t_env.explain_sql(
+ "SELECT count, word FROM MyTable1 WHERE word LIKE 'F%' "
+ "UNION ALL "
+ "SELECT count, word FROM MyTable2")
+print(explanation1)
+
+# explain SELECT statement through TableEnvironment.execute_sql()
+table_result = t_env.execute_sql(
+ "EXPLAIN PLAN FOR "
+ "SELECT count, word FROM MyTable1 WHERE word LIKE 'F%' "
+ "UNION ALL "
+ "SELECT count, word FROM MyTable2")
+table_result.print()
+
+{% endhighlight %}
+
+
+
+{% highlight sql %}
+Flink SQL> CREATE TABLE MyTable1 (count bigint, work VARCHAR(256);
+[INFO] Table has been created.
+
+Flink SQL> CREATE TABLE MyTable2 (count bigint, work VARCHAR(256);
+[INFO] Table has been created.
+
+Flink SQL> EXPLAIN PLAN FOR SELECT count, word FROM MyTable1 WHERE word LIKE 'F%'
+> UNION ALL
+> SELECT count, word FROM MyTable2;
+
+{% endhighlight %}
+
+
+{% top %}
+
+## 语法
+
+{% highlight sql %}
+EXPLAIN PLAN FOR
+{% endhighlight %}
+
+请参阅 [Queries]({{ site.baseurl }}/zh/dev/table/sql/queries.html#supported-syntax) 页面获得 query 的语法。
+请参阅 [INSERT]({{ site.baseurl }}/zh/dev/table/sql/insert.html) 页面获得 INSERT 的语法。
+
+{% top %}
diff --git a/docs/dev/table/sql/hints.md b/docs/dev/table/sql/hints.md
new file mode 100644
index 00000000000000..d8c4c900316e2c
--- /dev/null
+++ b/docs/dev/table/sql/hints.md
@@ -0,0 +1,88 @@
+---
+title: "SQL Hints"
+nav-parent_id: sql
+nav-pos: 6
+---
+
+
+* This will be replaced by the TOC
+{:toc}
+
+SQL hints can be used with SQL statements to alter execution plans. This chapter explains how to use hints to force various approaches.
+
+Generally a hint can be used to:
+
+- Enforce planner: there's no perfect planner, so it makes sense to implement hints to
+allow user better control the execution;
+- Append meta data(or statistics): some statistics like “table index for scan” and
+“skew info of some shuffle keys” are somewhat dynamic for the query, it would be very
+convenient to config them with hints because our planning metadata from the planner is very
+often not that accurate;
+- Operator resource constraints: for many cases, we would give a default resource
+configuration for the execution operators, i.e. min parallelism or
+managed memory (resource consuming UDF) or special resource requirement (GPU or SSD disk)
+and so on, it would be very flexible to profile the resource with hints per query(instead of the Job).
+
+## Dynamic Table Options
+Dynamic table options allows to specify or override table options dynamically, different with static table options defined with SQL DDL or connect API,
+these options can be specified flexibly in per-table scope within each query.
+
+Thus it is very suitable to use for the ad-hoc queries in interactive terminal, for example, in the SQL-CLI,
+you can specify to ignore the parse error for a CSV source just by adding a dynamic option `/*+ OPTIONS('csv.ignore-parse-errors'='true') */`.
+
+Note: Dynamic table options default is forbidden to use because it may change the semantics of the query.
+You need to set the config option `table.dynamic-table-options.enabled` to be `true` explicitly (default is false),
+See the Configuration for details on how to set up the config options.
+
+### Syntax
+In order to not break the SQL compatibility, we use the Oracle style SQL hint syntax:
+{% highlight sql %}
+table_path /*+ OPTIONS(key=val [, key=val]*) */
+
+key:
+ stringLiteral
+val:
+ stringLiteral
+
+{% endhighlight %}
+
+### Examples
+
+{% highlight sql %}
+
+CREATE TABLE kafka_table1 (id BIGINT, name STRING, age INT) WITH (...);
+CREATE TABLE kafka_table2 (id BIGINT, name STRING, age INT) WITH (...);
+
+-- override table options in query source
+select id, name from kafka_table1 /*+ OPTIONS('scan.startup.mode'='earliest-offset') */;
+
+-- override table options in join
+select * from
+ kafka_table1 /*+ OPTIONS('scan.startup.mode'='earliest-offset') */ t1
+ join
+ kafka_table2 /*+ OPTIONS('scan.startup.mode'='earliest-offset') */ t2
+ on t1.id = t2.id;
+
+-- override table options for INSERT target table
+insert into kafka_table1 /*+ OPTIONS('sink.partitioner'='round-robin') */ select * from kafka_table2;
+
+{% endhighlight %}
+
+{% top %}
\ No newline at end of file
diff --git a/docs/dev/table/sql/hints.zh.md b/docs/dev/table/sql/hints.zh.md
new file mode 100644
index 00000000000000..d8c4c900316e2c
--- /dev/null
+++ b/docs/dev/table/sql/hints.zh.md
@@ -0,0 +1,88 @@
+---
+title: "SQL Hints"
+nav-parent_id: sql
+nav-pos: 6
+---
+
+
+* This will be replaced by the TOC
+{:toc}
+
+SQL hints can be used with SQL statements to alter execution plans. This chapter explains how to use hints to force various approaches.
+
+Generally a hint can be used to:
+
+- Enforce planner: there's no perfect planner, so it makes sense to implement hints to
+allow user better control the execution;
+- Append meta data(or statistics): some statistics like “table index for scan” and
+“skew info of some shuffle keys” are somewhat dynamic for the query, it would be very
+convenient to config them with hints because our planning metadata from the planner is very
+often not that accurate;
+- Operator resource constraints: for many cases, we would give a default resource
+configuration for the execution operators, i.e. min parallelism or
+managed memory (resource consuming UDF) or special resource requirement (GPU or SSD disk)
+and so on, it would be very flexible to profile the resource with hints per query(instead of the Job).
+
+## Dynamic Table Options
+Dynamic table options allows to specify or override table options dynamically, different with static table options defined with SQL DDL or connect API,
+these options can be specified flexibly in per-table scope within each query.
+
+Thus it is very suitable to use for the ad-hoc queries in interactive terminal, for example, in the SQL-CLI,
+you can specify to ignore the parse error for a CSV source just by adding a dynamic option `/*+ OPTIONS('csv.ignore-parse-errors'='true') */`.
+
+Note: Dynamic table options default is forbidden to use because it may change the semantics of the query.
+You need to set the config option `table.dynamic-table-options.enabled` to be `true` explicitly (default is false),
+See the Configuration for details on how to set up the config options.
+
+### Syntax
+In order to not break the SQL compatibility, we use the Oracle style SQL hint syntax:
+{% highlight sql %}
+table_path /*+ OPTIONS(key=val [, key=val]*) */
+
+key:
+ stringLiteral
+val:
+ stringLiteral
+
+{% endhighlight %}
+
+### Examples
+
+{% highlight sql %}
+
+CREATE TABLE kafka_table1 (id BIGINT, name STRING, age INT) WITH (...);
+CREATE TABLE kafka_table2 (id BIGINT, name STRING, age INT) WITH (...);
+
+-- override table options in query source
+select id, name from kafka_table1 /*+ OPTIONS('scan.startup.mode'='earliest-offset') */;
+
+-- override table options in join
+select * from
+ kafka_table1 /*+ OPTIONS('scan.startup.mode'='earliest-offset') */ t1
+ join
+ kafka_table2 /*+ OPTIONS('scan.startup.mode'='earliest-offset') */ t2
+ on t1.id = t2.id;
+
+-- override table options for INSERT target table
+insert into kafka_table1 /*+ OPTIONS('sink.partitioner'='round-robin') */ select * from kafka_table2;
+
+{% endhighlight %}
+
+{% top %}
\ No newline at end of file
diff --git a/docs/dev/table/sql/index.md b/docs/dev/table/sql/index.md
index 1affb421cee517..63d549c7547b84 100644
--- a/docs/dev/table/sql/index.md
+++ b/docs/dev/table/sql/index.md
@@ -29,10 +29,15 @@ This page describes the SQL language supported in Flink, including Data Definiti
This page lists all the supported statements supported in Flink SQL for now:
- [SELECT (Queries)](queries.html)
-- [CREATE TABLE, DATABASE, FUNCTION](create.html)
-- [DROP TABLE, DATABASE, FUNCTION](drop.html)
+- [CREATE TABLE, DATABASE, VIEW, FUNCTION](create.html)
+- [DROP TABLE, DATABASE, VIEW, FUNCTION](drop.html)
- [ALTER TABLE, DATABASE, FUNCTION](alter.html)
- [INSERT](insert.html)
+- [SQL HINTS](hints.html)
+- [DESCRIBE](describe.html)
+- [EXPLAIN](explain.html)
+- [USE](use.html)
+- [SHOW](show.html)
## Data Types
diff --git a/docs/dev/table/sql/index.zh.md b/docs/dev/table/sql/index.zh.md
index 5cdc8d0ced07dc..0def8c2f79c88c 100644
--- a/docs/dev/table/sql/index.zh.md
+++ b/docs/dev/table/sql/index.zh.md
@@ -28,11 +28,16 @@ under the License.
本页面列出了目前 Flink SQL 所支持的所有语句:
-- [SELECT (查询)](queries.html)
-- [CREATE TABLE, DATABASE, FUNCTION](create.html)
-- [DROP TABLE, DATABASE, FUNCTION](drop.html)
+- [SELECT (Queries)](queries.html)
+- [CREATE TABLE, DATABASE, VIEW, FUNCTION](create.html)
+- [DROP TABLE, DATABASE, VIEW, FUNCTION](drop.html)
- [ALTER TABLE, DATABASE, FUNCTION](alter.html)
- [INSERT](insert.html)
+- [SQL HINTS](hints.html)
+- [DESCRIBE](describe.html)
+- [EXPLAIN](explain.html)
+- [USE](use.html)
+- [SHOW](show.html)
## 数据类型
diff --git a/docs/dev/table/sql/insert.md b/docs/dev/table/sql/insert.md
index 96052adff5945f..01fa413e05ddbf 100644
--- a/docs/dev/table/sql/insert.md
+++ b/docs/dev/table/sql/insert.md
@@ -29,9 +29,10 @@ INSERT statements are used to add rows to a table.
## Run an INSERT statement
-INSERT statements are specified with the `sqlUpdate()` method of the `TableEnvironment` or executed in [SQL CLI]({{ site.baseurl }}/dev/table/sqlClient.html). The method `sqlUpdate()` for INSERT statements is a lazy execution, they will be executed only when `TableEnvironment.execute(jobName)` is invoked.
+Single INSERT statement can be executed through the `executeSql()` method of the `TableEnvironment`, or executed in [SQL CLI]({{ site.baseurl }}/dev/table/sqlClient.html). The `executeSql()` method for INSERT statement will submit a Flink job immediately, and return a `TableResult` instance which associates the submitted job.
+Multiple INSERT statements can be executed through the `addInsertSql()` method of the `StatementSet` which can be created by the `TableEnvironment.createStatementSet()` method. The `addInsertSql()` method is a lazy execution, they will be executed only when `StatementSet.execute()` is invoked.
-The following examples show how to run an INSERT statement in `TableEnvironment` and in SQL CLI.
+The following examples show how to run a single INSERT statement in `TableEnvironment` and in SQL CLI, run multiple INSERT statements in `StatementSet`.
@@ -40,12 +41,31 @@ EnvironmentSettings settings = EnvironmentSettings.newInstance()...
TableEnvironment tEnv = TableEnvironment.create(settings);
// register a source table named "Orders" and a sink table named "RubberOrders"
-tEnv.sqlUpdate("CREATE TABLE Orders (`user` BIGINT, product VARCHAR, amount INT) WITH (...)");
-tEnv.sqlUpdate("CREATE TABLE RubberOrders(product VARCHAR, amount INT) WITH (...)");
+tEnv.executeSql("CREATE TABLE Orders (`user` BIGINT, product VARCHAR, amount INT) WITH (...)");
+tEnv.executeSql("CREATE TABLE RubberOrders(product VARCHAR, amount INT) WITH (...)");
-// run a SQL update query on the registered source table and emit the result to registered sink table
-tEnv.sqlUpdate(
+// run a single INSERT query on the registered source table and emit the result to registered sink table
+TableResult tableResult1 = tEnv.executeSql(
"INSERT INTO RubberOrders SELECT product, amount FROM Orders WHERE product LIKE '%Rubber%'");
+// get job status through TableResult
+System.out.println(tableResult1.getJobClient().get().getJobStatus());
+
+//----------------------------------------------------------------------------
+// register another sink table named "GlassOrders" for multiple INSERT queries
+tEnv.executeSql("CREATE TABLE GlassOrders(product VARCHAR, amount INT) WITH (...)");
+
+// run multiple INSERT queries on the registered source table and emit the result to registered sink tables
+StatementSet stmtSet = tEnv.createStatementSet();
+// only single INSERT query can be accepted by `addInsertSql` method
+stmtSet.addInsertSql(
+ "INSERT INTO RubberOrders SELECT product, amount FROM Orders WHERE product LIKE '%Rubber%'");
+stmtSet.addInsertSql(
+ "INSERT INTO GlassOrders SELECT product, amount FROM Orders WHERE product LIKE '%Glass%'");
+// execute all statements together
+TableResult tableResult2 = stmtSet.execute();
+// get job status through TableResult
+System.out.println(tableResult2.getJobClient().get().getJobStatus());
+
{% endhighlight %}
@@ -55,27 +75,65 @@ val settings = EnvironmentSettings.newInstance()...
val tEnv = TableEnvironment.create(settings)
// register a source table named "Orders" and a sink table named "RubberOrders"
-tEnv.sqlUpdate("CREATE TABLE Orders (`user` BIGINT, product STRING, amount INT) WITH (...)")
-tEnv.sqlUpdate("CREATE TABLE RubberOrders(product STRING, amount INT) WITH (...)")
+tEnv.executeSql("CREATE TABLE Orders (`user` BIGINT, product STRING, amount INT) WITH (...)")
+tEnv.executeSql("CREATE TABLE RubberOrders(product STRING, amount INT) WITH (...)")
-// run a SQL update query on the registered source table and emit the result to registered sink table
-tEnv.sqlUpdate(
+// run a single INSERT query on the registered source table and emit the result to registered sink table
+val tableResult1 = tEnv.executeSql(
"INSERT INTO RubberOrders SELECT product, amount FROM Orders WHERE product LIKE '%Rubber%'")
+// get job status through TableResult
+println(tableResult1.getJobClient().get().getJobStatus())
+
+//----------------------------------------------------------------------------
+// register another sink table named "GlassOrders" for multiple INSERT queries
+tEnv.executeSql("CREATE TABLE GlassOrders(product VARCHAR, amount INT) WITH (...)")
+
+// run multiple INSERT queries on the registered source table and emit the result to registered sink tables
+val stmtSet = tEnv.createStatementSet()
+// only single INSERT query can be accepted by `addInsertSql` method
+stmtSet.addInsertSql(
+ "INSERT INTO RubberOrders SELECT product, amount FROM Orders WHERE product LIKE '%Rubber%'")
+stmtSet.addInsertSql(
+ "INSERT INTO GlassOrders SELECT product, amount FROM Orders WHERE product LIKE '%Glass%'")
+// execute all statements together
+val tableResult2 = stmtSet.execute()
+// get job status through TableResult
+println(tableResult2.getJobClient().get().getJobStatus())
+
{% endhighlight %}
{% highlight python %}
-settings = EnvironmentSettings.newInstance()...
-table_env = TableEnvironment.create(settings)
+settings = EnvironmentSettings.new_instance()...
+table_env = StreamTableEnvironment.create(env, settings)
# register a source table named "Orders" and a sink table named "RubberOrders"
-table_env.sqlUpdate("CREATE TABLE Orders (`user` BIGINT, product STRING, amount INT) WITH (...)")
-table_env.sqlUpdate("CREATE TABLE RubberOrders(product STRING, amount INT) WITH (...)")
+table_env.execute_sql("CREATE TABLE Orders (`user` BIGINT, product STRING, amount INT) WITH (...)")
+table_env.execute_sql("CREATE TABLE RubberOrders(product STRING, amount INT) WITH (...)")
+
+# run a single INSERT query on the registered source table and emit the result to registered sink table
+table_result1 = table_env \
+ .execute_sql("INSERT INTO RubberOrders SELECT product, amount FROM Orders WHERE product LIKE '%Rubber%'")
+# get job status through TableResult
+print(table_result1get_job_client().get_job_status())
+
+#----------------------------------------------------------------------------
+# register another sink table named "GlassOrders" for multiple INSERT queries
+table_env.execute_sql("CREATE TABLE GlassOrders(product VARCHAR, amount INT) WITH (...)")
+
+# run multiple INSERT queries on the registered source table and emit the result to registered sink tables
+stmt_set = table_env.create_statement_set()
+# only single INSERT query can be accepted by `add_insert_sql` method
+stmt_set \
+ .add_insert_sql("INSERT INTO RubberOrders SELECT product, amount FROM Orders WHERE product LIKE '%Rubber%'")
+stmt_set \
+ .add_insert_sql("INSERT INTO GlassOrders SELECT product, amount FROM Orders WHERE product LIKE '%Glass%'")
+# execute all statements together
+table_result2 = stmt_set.execute()
+# get job status through TableResult
+print(table_result2.get_job_client().get_job_status())
-# run a SQL update query on the registered source table and emit the result to registered sink table
-table_env \
- .sqlUpdate("INSERT INTO RubberOrders SELECT product, amount FROM Orders WHERE product LIKE '%Rubber%'")
{% endhighlight %}
@@ -40,12 +41,31 @@ EnvironmentSettings settings = EnvironmentSettings.newInstance()...
TableEnvironment tEnv = TableEnvironment.create(settings);
// 注册一个 "Orders" 源表,和 "RubberOrders" 结果表
-tEnv.sqlUpdate("CREATE TABLE Orders (`user` BIGINT, product VARCHAR, amount INT) WITH (...)");
-tEnv.sqlUpdate("CREATE TABLE RubberOrders(product VARCHAR, amount INT) WITH (...)");
+tEnv.executeSql("CREATE TABLE Orders (`user` BIGINT, product VARCHAR, amount INT) WITH (...)");
+tEnv.executeSql("CREATE TABLE RubberOrders(product VARCHAR, amount INT) WITH (...)");
-// 运行一个 INSERT 语句,将源表的数据输出到结果表中
-tEnv.sqlUpdate(
+// 运行一条 INSERT 语句,将源表的数据输出到结果表中
+TableResult tableResult1 = tEnv.executeSql(
"INSERT INTO RubberOrders SELECT product, amount FROM Orders WHERE product LIKE '%Rubber%'");
+// 通过 TableResult 来获取作业状态
+System.out.println(tableResult1.getJobClient().get().getJobStatus());
+
+//----------------------------------------------------------------------------
+// 注册一个 "GlassOrders" 结果表用于运行多 INSERT 语句
+tEnv.executeSql("CREATE TABLE GlassOrders(product VARCHAR, amount INT) WITH (...)");
+
+// 运行多条 INSERT 语句,将原表数据输出到多个结果表中
+StatementSet stmtSet = tEnv.createStatementSet();
+// `addInsertSql` 方法每次只接收单条 INSERT 语句
+stmtSet.addInsertSql(
+ "INSERT INTO RubberOrders SELECT product, amount FROM Orders WHERE product LIKE '%Rubber%'");
+stmtSet.addInsertSql(
+ "INSERT INTO GlassOrders SELECT product, amount FROM Orders WHERE product LIKE '%Glass%'");
+// 执行刚刚添加的所有 INSERT 语句
+TableResult tableResult2 = stmtSet.execute();
+// 通过 TableResult 来获取作业状态
+System.out.println(tableResult1.getJobClient().get().getJobStatus());
+
{% endhighlight %}
@@ -55,27 +75,66 @@ val settings = EnvironmentSettings.newInstance()...
val tEnv = TableEnvironment.create(settings)
// 注册一个 "Orders" 源表,和 "RubberOrders" 结果表
-tEnv.sqlUpdate("CREATE TABLE Orders (`user` BIGINT, product STRING, amount INT) WITH (...)")
-tEnv.sqlUpdate("CREATE TABLE RubberOrders(product STRING, amount INT) WITH (...)")
+tEnv.executeSql("CREATE TABLE Orders (`user` BIGINT, product STRING, amount INT) WITH (...)")
+tEnv.executeSql("CREATE TABLE RubberOrders(product STRING, amount INT) WITH (...)")
// 运行一个 INSERT 语句,将源表的数据输出到结果表中
-tEnv.sqlUpdate(
+val tableResult1 = tEnv.executeSql(
"INSERT INTO RubberOrders SELECT product, amount FROM Orders WHERE product LIKE '%Rubber%'")
+// 通过 TableResult 来获取作业状态
+println(tableResult1.getJobClient().get().getJobStatus())
+
+//----------------------------------------------------------------------------
+// 注册一个 "GlassOrders" 结果表用于运行多 INSERT 语句
+tEnv.executeSql("CREATE TABLE GlassOrders(product VARCHAR, amount INT) WITH (...)");
+
+// 运行多个 INSERT 语句,将原表数据输出到多个结果表中
+val stmtSet = tEnv.createStatementSet()
+// `addInsertSql` 方法每次只接收单条 INSERT 语句
+stmtSet.addInsertSql(
+ "INSERT INTO RubberOrders SELECT product, amount FROM Orders WHERE product LIKE '%Rubber%'")
+stmtSet.addInsertSql(
+ "INSERT INTO GlassOrders SELECT product, amount FROM Orders WHERE product LIKE '%Glass%'")
+// 执行刚刚添加的所有 INSERT 语句
+val tableResult2 = stmtSet.execute()
+// 通过 TableResult 来获取作业状态
+println(tableResult1.getJobClient().get().getJobStatus())
+
{% endhighlight %}
{% highlight python %}
-settings = EnvironmentSettings.newInstance()...
-table_env = TableEnvironment.create(settings)
+settings = EnvironmentSettings.new_instance()...
+table_env = StreamTableEnvironment.create(env, settings)
# 注册一个 "Orders" 源表,和 "RubberOrders" 结果表
-table_env.sqlUpdate("CREATE TABLE Orders (`user` BIGINT, product STRING, amount INT) WITH (...)")
-table_env.sqlUpdate("CREATE TABLE RubberOrders(product STRING, amount INT) WITH (...)")
+table_env.executeSql("CREATE TABLE Orders (`user` BIGINT, product STRING, amount INT) WITH (...)")
+table_env.executeSql("CREATE TABLE RubberOrders(product STRING, amount INT) WITH (...)")
+
+# 运行一条 INSERT 语句,将源表的数据输出到结果表中
+table_result1 = table_env \
+ .executeSql("INSERT INTO RubberOrders SELECT product, amount FROM Orders WHERE product LIKE '%Rubber%'")
+# 通过 TableResult 来获取作业状态
+print(table_result1.get_job_client().get_job_status())
+
+#----------------------------------------------------------------------------
+# 注册一个 "GlassOrders" 结果表用于运行多 INSERT 语句
+table_env.execute_sql("CREATE TABLE GlassOrders(product VARCHAR, amount INT) WITH (...)")
+
+# 运行多条 INSERT 语句,将原表数据输出到多个结果表中
+stmt_set = table_env.create_statement_set()
+# `add_insert_sql` 方法每次只接收单条 INSERT 语句
+stmt_set \
+ .add_insert_sql("INSERT INTO RubberOrders SELECT product, amount FROM Orders WHERE product LIKE '%Rubber%'")
+stmt_set \
+ .add_insert_sql("INSERT INTO GlassOrders SELECT product, amount FROM Orders WHERE product LIKE '%Glass%'")
+# 执行刚刚添加的所有 INSERT 语句
+table_result2 = stmt_set.execute()
+# 通过 TableResult 来获取作业状态
+print(table_result2.get_job_client().get_job_status())
+
-# 运行一个 INSERT 语句,将源表的数据输出到结果表中
-table_env \
- .sqlUpdate("INSERT INTO RubberOrders SELECT product, amount FROM Orders WHERE product LIKE '%Rubber%'")
{% endhighlight %}
diff --git a/docs/dev/table/sql/queries.md b/docs/dev/table/sql/queries.md
index f860d5a10bb659..df78622cd5a5da 100644
--- a/docs/dev/table/sql/queries.md
+++ b/docs/dev/table/sql/queries.md
@@ -25,7 +25,7 @@ under the License.
* This will be replaced by the TOC
{:toc}
-SELECT queries are specified with the `sqlQuery()` method of the `TableEnvironment`. The method returns the result of the SELECT query as a `Table`. A `Table` can be used in [subsequent SQL and Table API queries]({{ site.baseurl }}/dev/table/common.html#mixing-table-api-and-sql), be [converted into a DataSet or DataStream]({{ site.baseurl }}/dev/table/common.html#integration-with-datastream-and-dataset-api), or [written to a TableSink]({{ site.baseurl }}/dev/table/common.html#emit-a-table). SQL and Table API queries can be seamlessly mixed and are holistically optimized and translated into a single program.
+SELECT statements and VALUES statements are specified with the `sqlQuery()` method of the `TableEnvironment`. The method returns the result of the SELECT statement (or the VALUES statements) as a `Table`. A `Table` can be used in [subsequent SQL and Table API queries]({{ site.baseurl }}/dev/table/common.html#mixing-table-api-and-sql), be [converted into a DataSet or DataStream]({{ site.baseurl }}/dev/table/common.html#integration-with-datastream-and-dataset-api), or [written to a TableSink]({{ site.baseurl }}/dev/table/common.html#emit-a-table). SQL and Table API queries can be seamlessly mixed and are holistically optimized and translated into a single program.
In order to access a table in a SQL query, it must be [registered in the TableEnvironment]({{ site.baseurl }}/dev/table/common.html#register-tables-in-the-catalog). A table can be registered from a [TableSource]({{ site.baseurl }}/dev/table/common.html#register-a-tablesource), [Table]({{ site.baseurl }}/dev/table/common.html#register-a-table), [CREATE TABLE statement](#create-table), [DataStream, or DataSet]({{ site.baseurl }}/dev/table/common.html#register-a-datastream-or-dataset-as-table). Alternatively, users can also [register catalogs in a TableEnvironment]({{ site.baseurl }}/dev/table/catalogs.html) to specify the location of the data sources.
@@ -58,7 +58,6 @@ tableEnv.createTemporaryView("Orders", ds, $("user"), $("product"), $("amount"))
Table result2 = tableEnv.sqlQuery(
"SELECT product, amount FROM Orders WHERE product LIKE '%Rubber%'");
-// SQL update with a registered table
// create and register a TableSink
final Schema schema = new Schema()
.field("product", DataTypes.STRING())
@@ -69,8 +68,8 @@ tableEnv.connect(new FileSystem("/path/to/file"))
.withSchema(schema)
.createTemporaryTable("RubberOrders");
-// run a SQL update query on the Table and emit the result to the TableSink
-tableEnv.sqlUpdate(
+// run an INSERT SQL on the Table and emit the result to the TableSink
+tableEnv.executeSql(
"INSERT INTO RubberOrders SELECT product, amount FROM Orders WHERE product LIKE '%Rubber%'");
{% endhighlight %}
@@ -95,7 +94,6 @@ tableEnv.createTemporaryView("Orders", ds, $"user", $"product", $"amount")
val result2 = tableEnv.sqlQuery(
"SELECT product, amount FROM Orders WHERE product LIKE '%Rubber%'")
-// SQL update with a registered table
// create and register a TableSink
val schema = new Schema()
.field("product", DataTypes.STRING())
@@ -106,8 +104,8 @@ tableEnv.connect(new FileSystem("/path/to/file"))
.withSchema(schema)
.createTemporaryTable("RubberOrders")
-// run a SQL update query on the Table and emit the result to the TableSink
-tableEnv.sqlUpdate(
+// run an INSERT SQL on the Table and emit the result to the TableSink
+tableEnv.executeSql(
"INSERT INTO RubberOrders SELECT product, amount FROM Orders WHERE product LIKE '%Rubber%'")
{% endhighlight %}
@@ -123,7 +121,6 @@ table = table_env.from_elements(..., ['user', 'product', 'amount'])
result = table_env \
.sql_query("SELECT SUM(amount) FROM %s WHERE product LIKE '%%Rubber%%'" % table)
-# SQL update with a registered table
# create and register a TableSink
t_env.connect(FileSystem().path("/path/to/file")))
.with_format(Csv()
@@ -134,16 +131,107 @@ t_env.connect(FileSystem().path("/path/to/file")))
.field("amount", DataTypes.BIGINT()))
.create_temporary_table("RubberOrders")
-# run a SQL update query on the Table and emit the result to the TableSink
+# run an INSERT SQL on the Table and emit the result to the TableSink
table_env \
- .sql_update("INSERT INTO RubberOrders SELECT product, amount FROM Orders WHERE product LIKE '%Rubber%'")
+ .execute_sql("INSERT INTO RubberOrders SELECT product, amount FROM Orders WHERE product LIKE '%Rubber%'")
{% endhighlight %}
{% top %}
-## Supported Syntax
+## Execute a Query
+A SELECT statement or a VALUES statement can be executed to collect the content to local through the `TableEnvironment.executeSql()` method. The method returns the result of the SELECT statement (or the VALUES statement) as a `TableResult`. Similar to a SELECT statement, a `Table` object can be executed using the `Table.execute()` method to collect the content of the query to the local client.
+`TableResult.collect()` method returns a closeable row iterator. The select job will not be finished unless all result data has been collected. We should actively close the job to avoid resource leak through the `CloseableIterator#close()` method.
+We can also print the select result to client console through the `TableResult.print()` method. The result data in `TableResult` can be accessed only once. Thus, `collect()` and `print()` must not be called after each other.
+
+For streaming job, `TableResult.collect()` method or `TableResult.print` method guarantee end-to-end exactly-once record delivery. This requires the checkpointing mechanism to be enabled. By default, checkpointing is disabled. To enable checkpointing, we can set checkpointing properties (see the checkpointing config for details) through `TableConfig`.
+So a result record can be accessed by client only after its corresponding checkpoint completes.
+
+**Notes:** For streaming mode, only append-only query is supported now.
+
+
+
+{% highlight java %}
+StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
+StreamTableEnvironment tableEnv = StreamTableEnvironment.create(env, settings);
+// enable checkpointing
+tableEnv.getConfig().getConfiguration().set(
+ ExecutionCheckpointingOptions.CHECKPOINTING_MODE, CheckpointingMode.EXACTLY_ONCE);
+tableEnv.getConfig().getConfiguration().set(
+ ExecutionCheckpointingOptions.CHECKPOINTING_INTERVAL, Duration.ofSeconds(10));
+
+tableEnv.executeSql("CREATE TABLE Orders (`user` BIGINT, product STRING, amount INT) WITH (...)");
+
+// execute SELECT statement
+TableResult tableResult1 = tableEnv.executeSql("SELECT * FROM Orders");
+// use try-with-resources statement to make sure the iterator will be closed automatically
+try (CloseableIterator it = tableResult1.collect()) {
+ while(it.hasNext()) {
+ Row row = it.next();
+ // handle row
+ }
+}
+
+// execute Table
+TableResult tableResult2 = tableEnv.sqlQuery("SELECT * FROM Orders").execute();
+tableResult2.print();
+
+{% endhighlight %}
+
@@ -566,15 +612,15 @@ FROM Orders FULL OUTER JOIN Product ON Orders.productId = Product.id
-
Time-windowed Join
+
Interval Join BatchStreaming
-
Note: Time-windowed joins are a subset of regular joins that can be processed in a streaming fashion.
+
Note: Interval joins are a subset of regular joins that can be processed in a streaming fashion.
-
A time-windowed join requires at least one equi-join predicate and a join condition that bounds the time on both sides. Such a condition can be defined by two appropriate range predicates (<, <=, >=, >), a BETWEEN predicate, or a single equality predicate that compares time attributes of the same type (i.e., processing time or event time) of both input tables.
-
For example, the following predicates are valid window join conditions:
+
A interval join requires at least one equi-join predicate and a join condition that bounds the time on both sides. Such a condition can be defined by two appropriate range predicates (<, <=, >=, >), a BETWEEN predicate, or a single equality predicate that compares time attributes of the same type (i.e., processing time or event time) of both input tables.
+
For example, the following predicates are valid interval join conditions:
ltime = rtime
@@ -1127,7 +1173,7 @@ The start and end timestamps of group windows as well as time attributes can be
SESSION_END(time_attr, interval)
Returns the timestamp of the exclusive upper bound of the corresponding tumbling, hopping, or session window.
@@ -1144,7 +1190,7 @@ The start and end timestamps of group windows as well as time attributes can be
HOP_PROCTIME(time_attr, interval, interval) SESSION_PROCTIME(time_attr, interval)
diff --git a/docs/dev/table/sql/show.md b/docs/dev/table/sql/show.md
new file mode 100644
index 00000000000000..4c9e2419e907cb
--- /dev/null
+++ b/docs/dev/table/sql/show.md
@@ -0,0 +1,277 @@
+---
+title: "SHOW Statements"
+nav-parent_id: sql
+nav-pos: 10
+---
+
+
+* This will be replaced by the TOC
+{:toc}
+
+SHOW statements are used to list all catalogs, or list all databases in the current catalog, or list all tables/views in the current catalog and the current database, or list all functions including temp system functions, system functions, temp catalog functions and catalog functions in the current catalog and the current database.
+
+Flink SQL supports the following SHOW statements for now:
+- SHOW CATALOGS
+- SHOW DATABASES
+- SHOW TABLES
+- SHOW VIEWS
+- SHOW FUNCTIONS
+
+
+## Run a SHOW statement
+
+SHOW statements can be executed with the `executeSql()` method of the `TableEnvironment`, or executed in [SQL CLI]({{ site.baseurl }}/dev/table/sqlClient.html). The `executeSql()` method returns objects for a successful SHOW operation, otherwise will throw an exception.
+
+The following examples show how to run a SHOW statement in `TableEnvironment` and in SQL CLI.
+
+
+
+{% highlight java %}
+StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
+StreamTableEnvironment tEnv = StreamTableEnvironment.create(env);
+
+// show catalogs
+tEnv.executeSql("SHOW CATALOGS").print();
+// +-----------------+
+// | catalog name |
+// +-----------------+
+// | default_catalog |
+// +-----------------+
+
+// show databases
+tEnv.executeSql("SHOW DATABASES").print();
+// +------------------+
+// | database name |
+// +------------------+
+// | default_database |
+// +------------------+
+
+// create a table
+tEnv.executeSql("CREATE TABLE my_table (...) WITH (...)");
+// show tables
+tEnv.executeSql("SHOW TABLES").print();
+// +------------+
+// | table name |
+// +------------+
+// | my_table |
+// +------------+
+
+// create a view
+tEnv.executeSql("CREATE VIEW my_view AS ...");
+// show views
+tEnv.executeSql("SHOW VIEWS").print();
+// +-----------+
+// | view name |
+// +-----------+
+// | my_view |
+// +-----------+
+
+// show functions
+tEnv.executeSql("SHOW FUNCTIONS").print();
+// +---------------+
+// | function name |
+// +---------------+
+// | mod |
+// | sha256 |
+// | ... |
+// +---------------+
+
+{% endhighlight %}
+
+
+
+{% highlight scala %}
+val env = StreamExecutionEnvironment.getExecutionEnvironment()
+val tEnv = StreamTableEnvironment.create(env)
+
+// show catalogs
+tEnv.executeSql("SHOW CATALOGS").print()
+// +-----------------+
+// | catalog name |
+// +-----------------+
+// | default_catalog |
+// +-----------------+
+
+// show databases
+tEnv.executeSql("SHOW DATABASES").print()
+// +------------------+
+// | database name |
+// +------------------+
+// | default_database |
+// +------------------+
+
+// create a table
+tEnv.executeSql("CREATE TABLE my_table (...) WITH (...)")
+// show tables
+tEnv.executeSql("SHOW TABLES").print()
+// +------------+
+// | table name |
+// +------------+
+// | my_table |
+// +------------+
+
+// create a view
+tEnv.executeSql("CREATE VIEW my_view AS ...")
+// show views
+tEnv.executeSql("SHOW VIEWS").print()
+// +-----------+
+// | view name |
+// +-----------+
+// | my_view |
+// +-----------+
+
+// show functions
+tEnv.executeSql("SHOW FUNCTIONS").print()
+// +---------------+
+// | function name |
+// +---------------+
+// | mod |
+// | sha256 |
+// | ... |
+// +---------------+
+
+{% endhighlight %}
+
+
+
+{% highlight python %}
+settings = EnvironmentSettings.new_instance()...
+table_env = StreamTableEnvironment.create(env, settings)
+
+# show catalogs
+table_env.execute_sql("SHOW CATALOGS").print()
+# +-----------------+
+# | catalog name |
+# +-----------------+
+# | default_catalog |
+# +-----------------+
+
+# show databases
+table_env.execute_sql("SHOW DATABASES").print()
+# +------------------+
+# | database name |
+# +------------------+
+# | default_database |
+# +------------------+
+
+# create a table
+table_env.execute_sql("CREATE TABLE my_table (...) WITH (...)")
+# show tables
+table_env.execute_sql("SHOW TABLES").print()
+# +------------+
+# | table name |
+# +------------+
+# | my_table |
+# +------------+
+
+# create a view
+table_env.execute_sql("CREATE VIEW my_view AS ...")
+# show views
+table_env.execute_sql("SHOW VIEWS").print()
+# +-----------+
+# | view name |
+# +-----------+
+# | my_view |
+# +-----------+
+
+# show functions
+table_env.execute_sql("SHOW FUNCTIONS").print()
+# +---------------+
+# | function name |
+# +---------------+
+# | mod |
+# | sha256 |
+# | ... |
+# +---------------+
+
+{% endhighlight %}
+
+
+
+{% highlight sql %}
+
+Flink SQL> SHOW CATALOGS;
+default_catalog
+
+Flink SQL> SHOW DATABASES;
+default_database
+
+Flink SQL> CREATE TABLE my_table (...) WITH (...);
+[INFO] Table has been created.
+
+Flink SQL> SHOW TABLES;
+my_table
+
+Flink SQL> CREATE VIEW my_view AS ...;
+[INFO] View has been created.
+
+Flink SQL> SHOW VIEWS;
+my_view
+
+Flink SQL> SHOW FUNCTIONS;
+mod
+sha256
+...
+
+{% endhighlight %}
+
+
+
+{% top %}
+
+## SHOW CATALOGS
+
+{% highlight sql %}
+SHOW CATALOGS
+{% endhighlight %}
+
+Show all catalogs.
+
+## SHOW DATABASES
+
+{% highlight sql %}
+SHOW DATABASES
+{% endhighlight %}
+
+Show all databases in the current catalog.
+
+## SHOW TABLES
+
+{% highlight sql %}
+SHOW TABLES
+{% endhighlight %}
+
+Show all tables in the current catalog and the current database.
+
+## SHOW VIEWS
+
+{% highlight sql %}
+SHOW VIEWS
+{% endhighlight %}
+
+Show all views in the current catalog and the current database.
+
+## SHOW FUNCTIONS
+
+{% highlight sql %}
+SHOW FUNCTIONS
+{% endhighlight %}
+
+Show all functions including temp system functions, system functions, temp catalog functions and catalog functions in the current catalog and current database.
\ No newline at end of file
diff --git a/docs/dev/table/sql/show.zh.md b/docs/dev/table/sql/show.zh.md
new file mode 100644
index 00000000000000..9f6c648330fd5e
--- /dev/null
+++ b/docs/dev/table/sql/show.zh.md
@@ -0,0 +1,277 @@
+---
+title: "SHOW 语句"
+nav-parent_id: sql
+nav-pos: 10
+---
+
+
+* This will be replaced by the TOC
+{:toc}
+
+SHOW 语句用于列出所有的 catalog,或者列出当前 catalog 中所有的 database,或者列出当前 catalog 和当前 database 的所有表或视图,或者列出所有的 function,包括:临时系统 function,系统 function,临时 catalog function,当前 catalog 和 database 中的 catalog function。
+
+目前 Flink SQL 支持下列 SHOW 语句:
+- SHOW CATALOGS
+- SHOW DATABASES
+- SHOW TABLES
+- SHOW VIEWS
+- SHOW FUNCTIONS
+
+
+## 执行 SHOW 语句
+
+可以使用 `TableEnvironment` 中的 `executeSql()` 方法执行 SHOW 语句,也可以在 [SQL CLI]({{ site.baseurl }}/zh/dev/table/sqlClient.html) 中执行 SHOW 语句。 若 SHOW 操作执行成功,`executeSql()` 方法返回所有对象,否则会抛出异常。
+
+以下的例子展示了如何在 `TableEnvironment` 和 SQL CLI 中执行一个 SHOW 语句。
+
+
+
+{% highlight java %}
+StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
+StreamTableEnvironment tEnv = StreamTableEnvironment.create(env);
+
+// show catalogs
+tEnv.executeSql("SHOW CATALOGS").print();
+// +-----------------+
+// | catalog name |
+// +-----------------+
+// | default_catalog |
+// +-----------------+
+
+// show databases
+tEnv.executeSql("SHOW DATABASES").print();
+// +------------------+
+// | database name |
+// +------------------+
+// | default_database |
+// +------------------+
+
+// create a table
+tEnv.executeSql("CREATE TABLE my_table (...) WITH (...)");
+// show tables
+tEnv.executeSql("SHOW TABLES").print();
+// +------------+
+// | table name |
+// +------------+
+// | my_table |
+// +------------+
+
+// create a view
+tEnv.executeSql("CREATE VIEW my_view AS ...");
+// show views
+tEnv.executeSql("SHOW VIEWS").print();
+// +-----------+
+// | view name |
+// +-----------+
+// | my_view |
+// +-----------+
+
+// show functions
+tEnv.executeSql("SHOW FUNCTIONS").print();
+// +---------------+
+// | function name |
+// +---------------+
+// | mod |
+// | sha256 |
+// | ... |
+// +---------------+
+
+{% endhighlight %}
+
+
+
+{% highlight scala %}
+val env = StreamExecutionEnvironment.getExecutionEnvironment()
+val tEnv = StreamTableEnvironment.create(env)
+
+// show catalogs
+tEnv.executeSql("SHOW CATALOGS").print()
+// +-----------------+
+// | catalog name |
+// +-----------------+
+// | default_catalog |
+// +-----------------+
+
+// show databases
+tEnv.executeSql("SHOW DATABASES").print()
+// +------------------+
+// | database name |
+// +------------------+
+// | default_database |
+// +------------------+
+
+// create a table
+tEnv.executeSql("CREATE TABLE my_table (...) WITH (...)")
+// show tables
+tEnv.executeSql("SHOW TABLES").print()
+// +------------+
+// | table name |
+// +------------+
+// | my_table |
+// +------------+
+
+// create a view
+tEnv.executeSql("CREATE VIEW my_view AS ...")
+// show views
+tEnv.executeSql("SHOW VIEWS").print()
+// +-----------+
+// | view name |
+// +-----------+
+// | my_view |
+// +-----------+
+
+// show functions
+tEnv.executeSql("SHOW FUNCTIONS").print()
+// +---------------+
+// | function name |
+// +---------------+
+// | mod |
+// | sha256 |
+// | ... |
+// +---------------+
+
+{% endhighlight %}
+
+
+
+{% highlight python %}
+settings = EnvironmentSettings.new_instance()...
+table_env = StreamTableEnvironment.create(env, settings)
+
+# show catalogs
+table_env.execute_sql("SHOW CATALOGS").print()
+# +-----------------+
+# | catalog name |
+# +-----------------+
+# | default_catalog |
+# +-----------------+
+
+# show databases
+table_env.execute_sql("SHOW DATABASES").print()
+# +------------------+
+# | database name |
+# +------------------+
+# | default_database |
+# +------------------+
+
+# create a table
+table_env.execute_sql("CREATE TABLE my_table (...) WITH (...)")
+# show tables
+table_env.execute_sql("SHOW TABLES").print()
+# +------------+
+# | table name |
+# +------------+
+# | my_table |
+# +------------+
+
+# create a view
+table_env.execute_sql("CREATE VIEW my_view AS ...")
+# show views
+table_env.execute_sql("SHOW VIEWS").print()
+# +-----------+
+# | view name |
+# +-----------+
+# | my_view |
+# +-----------+
+
+# show functions
+table_env.execute_sql("SHOW FUNCTIONS").print()
+# +---------------+
+# | function name |
+# +---------------+
+# | mod |
+# | sha256 |
+# | ... |
+# +---------------+
+
+{% endhighlight %}
+
+
+
+{% highlight sql %}
+
+Flink SQL> SHOW CATALOGS;
+default_catalog
+
+Flink SQL> SHOW DATABASES;
+default_database
+
+Flink SQL> CREATE TABLE my_table (...) WITH (...);
+[INFO] Table has been created.
+
+Flink SQL> SHOW TABLES;
+my_table
+
+Flink SQL> CREATE VIEW my_view AS ...;
+[INFO] View has been created.
+
+Flink SQL> SHOW VIEWS;
+my_view
+
+Flink SQL> SHOW FUNCTIONS;
+mod
+sha256
+...
+
+{% endhighlight %}
+
+
+
+{% top %}
+
+## SHOW CATALOGS
+
+{% highlight sql %}
+SHOW CATALOGS
+{% endhighlight %}
+
+展示所有的 catalog。
+
+## SHOW DATABASES
+
+{% highlight sql %}
+SHOW DATABASES
+{% endhighlight %}
+
+展示当前 catalog 中所有的 database。
+
+## SHOW TABLES
+
+{% highlight sql %}
+SHOW TABLES
+{% endhighlight %}
+
+展示当前 catalog 和当前 database 中所有的表。
+
+## SHOW VIEWS
+
+{% highlight sql %}
+SHOW VIEWS
+{% endhighlight %}
+
+展示当前 catalog 和当前 database 中所有的视图。
+
+## SHOW FUNCTIONS
+
+{% highlight sql %}
+SHOW FUNCTIONS
+{% endhighlight %}
+
+展示所有的 function,包括:临时系统 function, 系统 function, 临时 catalog function,当前 catalog 和 database 中的 catalog function。
\ No newline at end of file
diff --git a/docs/dev/table/sql/use.md b/docs/dev/table/sql/use.md
new file mode 100644
index 00000000000000..983d2d4b310fc7
--- /dev/null
+++ b/docs/dev/table/sql/use.md
@@ -0,0 +1,200 @@
+---
+title: "USE Statements"
+nav-parent_id: sql
+nav-pos: 9
+---
+
+
+* This will be replaced by the TOC
+{:toc}
+
+USE statements are used to set the current database or catalog.
+
+
+## Run a USE statement
+
+USE statements can be executed with the `executeSql()` method of the `TableEnvironment`, or executed in [SQL CLI]({{ site.baseurl }}/dev/table/sqlClient.html). The `executeSql()` method returns 'OK' for a successful USE operation, otherwise will throw an exception.
+
+The following examples show how to run a USE statement in `TableEnvironment` and in SQL CLI.
+
+
+{% highlight sql %}
+Flink SQL> CREATE CATALOG cat1 WITH (...);
+[INFO] Catalog has been created.
+
+Flink SQL> SHOW CATALOGS;
+default_catalog
+cat1
+
+Flink SQL> USE CATALOG cat1;
+
+Flink SQL> SHOW DATABASES;
+
+Flink SQL> CREATE DATABASE db1 WITH (...);
+[INFO] Database has been created.
+
+Flink SQL> SHOW DATABASES;
+db1
+
+Flink SQL> USE db1;
+
+{% endhighlight %}
+
+
+
+{% top %}
+
+## USE CATLOAG
+
+{% highlight sql %}
+USE CATALOG catalog_name
+{% endhighlight %}
+
+Set the current catalog. All subsequent commands that do not explicitly specify a catalog will use this one. If the provided catalog does not exist, an exception is thrown. The default current catalog is `default_catalog`.
+
+
+## USE
+
+{% highlight sql %}
+USE [catalog_name.]database_name
+{% endhighlight %}
+
+Set the current database. All subsequent commands that do not explicitly specify a database will use this one. If the provided database does not exist, an exception is thrown. The default current database is `default_database`.
diff --git a/docs/dev/table/sql/use.zh.md b/docs/dev/table/sql/use.zh.md
new file mode 100644
index 00000000000000..128faf521c9201
--- /dev/null
+++ b/docs/dev/table/sql/use.zh.md
@@ -0,0 +1,199 @@
+---
+title: "USE 语句"
+nav-parent_id: sql
+nav-pos: 9
+---
+
+
+* This will be replaced by the TOC
+{:toc}
+
+USE 语句用来设置当前的 catalog 或者 database。
+
+## 运行一个 USE 语句
+
+可以使用 TableEnvironment 中的 executeSql() 方法执行 USE 语句,也可以在 SQL CLI 中执行 USE 语句。 若 USE 操作执行成功,executeSql() 方法返回 'OK',否则会抛出异常。
+
+以下的例子展示了如何在 TableEnvironment 和 SQL CLI 中执行一个 USE 语句。
+
+
+{% highlight sql %}
+Flink SQL> CREATE CATALOG cat1 WITH (...);
+[INFO] Catalog has been created.
+
+Flink SQL> SHOW CATALOGS;
+default_catalog
+cat1
+
+Flink SQL> USE CATALOG cat1;
+
+Flink SQL> SHOW DATABASES;
+
+Flink SQL> CREATE DATABASE db1 WITH (...);
+[INFO] Database has been created.
+
+Flink SQL> SHOW DATABASES;
+db1
+
+Flink SQL> USE db1;
+
+{% endhighlight %}
+
+
+
+{% top %}
+
+## USE CATLOAG
+
+{% highlight sql %}
+USE CATALOG catalog_name
+{% endhighlight %}
+
+设置当前的 catalog。所有后续命令未显式指定 catalog 的将使用此 catalog。如果指定的的 catalog 不存在,则抛出异常。默认的当前 catalog 是 `default_catalog`。
+
+
+## USE
+
+{% highlight sql %}
+USE [catalog_name.]database_name
+{% endhighlight %}
+
+设置当前的 database。所有后续命令未显式指定 database 的将使用此 database。如果指定的的 database 不存在,则抛出异常。默认的当前 database 是 `default_database`。
diff --git a/docs/dev/table/sqlClient.md b/docs/dev/table/sqlClient.md
index 8331efef472bc2..ad1030855b576a 100644
--- a/docs/dev/table/sqlClient.md
+++ b/docs/dev/table/sqlClient.md
@@ -66,7 +66,7 @@ SELECT 'Hello World';
This query requires no table source and produces a single row result. The CLI will retrieve results from the cluster and visualize them. You can close the result view by pressing the `Q` key.
-The CLI supports **two modes** for maintaining and visualizing results.
+The CLI supports **three modes** for maintaining and visualizing results.
The **table mode** materializes results in memory and visualizes them in a regular, paginated table representation. It can be enabled by executing the following command in the CLI:
@@ -80,7 +80,18 @@ The **changelog mode** does not materialize results and visualizes the result st
SET execution.result-mode=changelog;
{% endhighlight %}
-You can use the following query to see both result modes in action:
+The **tableau mode** is more like a traditional way which will display the results in the screen directly with a tableau format.
+The displaying content will be influenced by the query execution type(`execution.type`).
+
+{% highlight text %}
+SET execution.result-mode=tableau;
+{% endhighlight %}
+
+Note that when you use this mode with streaming query, the result will be continuously printed on the console. If the input data of
+this query is bounded, the job will terminate after Flink processed all input data, and the printing will also be stopped automatically.
+Otherwise, if you want to terminate a running query, just type `CTRL-C` in this case, the job and the printing will be stopped.
+
+You can use the following query to see all the result modes in action:
{% highlight sql %}
SELECT name, COUNT(*) AS cnt FROM (VALUES ('Bob'), ('Alice'), ('Greg'), ('Bob')) AS NameTable(name) GROUP BY name;
@@ -106,9 +117,35 @@ Alice, 1
Greg, 1
{% endhighlight %}
-Both result modes can be useful during the prototyping of SQL queries. In both modes, results are stored in the Java heap memory of the SQL Client. In order to keep the CLI interface responsive, the changelog mode only shows the latest 1000 changes. The table mode allows for navigating through bigger results that are only limited by the available main memory and the configured [maximum number of rows](sqlClient.html#configuration) (`max-table-result-rows`).
+In *tableau mode*, if you ran the query in streaming mode, the displayed result would be:
+{% highlight text %}
++-----+----------------------+----------------------+
+| +/- | name | cnt |
++-----+----------------------+----------------------+
+| + | Bob | 1 |
+| + | Alice | 1 |
+| + | Greg | 1 |
+| - | Bob | 1 |
+| + | Bob | 2 |
++-----+----------------------+----------------------+
+Received a total of 5 rows
+{% endhighlight %}
+
+And if you ran the query in batch mode, the displayed result would be:
+{% highlight text %}
++-------+-----+
+| name | cnt |
++-------+-----+
+| Alice | 1 |
+| Bob | 2 |
+| Greg | 1 |
++-------+-----+
+3 rows in set
+{% endhighlight %}
+
+All these result modes can be useful during the prototyping of SQL queries. In all these modes, results are stored in the Java heap memory of the SQL Client. In order to keep the CLI interface responsive, the changelog mode only shows the latest 1000 changes. The table mode allows for navigating through bigger results that are only limited by the available main memory and the configured [maximum number of rows](sqlClient.html#configuration) (`max-table-result-rows`).
-Attention Queries that are executed in a batch environment, can only be retrieved using the `table` result mode.
+Attention Queries that are executed in a batch environment, can only be retrieved using the `table` or `tableau` result mode.
After a query is defined, it can be submitted to the cluster as a long-running, detached Flink job. For this, a target system that stores the results needs to be specified using the [INSERT INTO statement](sqlClient.html#detached-sql-queries). The [configuration section](sqlClient.html#configuration) explains how to declare table sources for reading data, how to declare table sinks for writing data, and how to configure other table program properties.
@@ -254,16 +291,16 @@ tables:
type: csv
fields:
- name: MyField1
- type: INT
+ data-type: INT
- name: MyField2
- type: VARCHAR
+ data-type: VARCHAR
line-delimiter: "\n"
comment-prefix: "#"
schema:
- name: MyField1
- type: INT
+ data-type: INT
- name: MyField2
- type: VARCHAR
+ data-type: VARCHAR
- name: MyCustomView
type: view
query: "SELECT MyField2 FROM MyTableSource"
diff --git a/docs/dev/table/sqlClient.zh.md b/docs/dev/table/sqlClient.zh.md
index c234390df014f3..7210f405a3eeec 100644
--- a/docs/dev/table/sqlClient.zh.md
+++ b/docs/dev/table/sqlClient.zh.md
@@ -65,7 +65,7 @@ SELECT 'Hello World';
该查询不需要 table source,并且只产生一行结果。CLI 将从集群中检索结果并将其可视化。按 `Q` 键退出结果视图。
-CLI 为维护和可视化结果提供**两种模式**。
+CLI 为维护和可视化结果提供**三种模式**。
**表格模式**(table mode)在内存中实体化结果,并将结果用规则的分页表格可视化展示出来。执行如下命令启用:
@@ -79,7 +79,18 @@ SET execution.result-mode=table;
SET execution.result-mode=changelog;
{% endhighlight %}
-你可以用如下查询来查看两种结果模式的运行情况:
+**Tableau模式**(tableau mode)更接近传统的数据库,会将执行的结果以制表的形式直接打在屏幕之上。具体显示的内容会取决于作业
+执行模式的不同(`execution.type`):
+
+{% highlight text %}
+SET execution.result-mode=tableau;
+{% endhighlight %}
+
+注意当你使用这个模式运行一个流式查询的时候,Flink 会将结果持续的打印在当前的屏幕之上。如果这个流式查询的输入是有限的数据集,
+那么Flink在处理完所有的数据之后,会自动的停止作业,同时屏幕上的打印也会相应的停止。如果你想提前结束这个查询,那么可以直接使用
+`CTRL-C` 按键,这个会停掉作业同时停止屏幕上的打印。
+
+你可以用如下查询来查看三种结果模式的运行情况:
{% highlight sql %}
SELECT name, COUNT(*) AS cnt FROM (VALUES ('Bob'), ('Alice'), ('Greg'), ('Bob')) AS NameTable(name) GROUP BY name;
@@ -105,9 +116,35 @@ Alice, 1
Greg, 1
{% endhighlight %}
-这两种结果模式在 SQL 查询的原型设计过程中都非常有用。这两种模式结果都存储在 SQL 客户端 的 Java 堆内存中。为了保持 CLI 界面及时响应,变更日志模式仅显示最近的 1000 个更改。表格模式支持浏览更大的结果,这些结果仅受可用主内存和配置的[最大行数](sqlClient.html#configuration)(`max-table-result-rows`)的限制。
+*Tableau模式* 下,如果这个查询以流的方式执行,那么将显示以下内容:
+{% highlight text %}
++-----+----------------------+----------------------+
+| +/- | name | cnt |
++-----+----------------------+----------------------+
+| + | Bob | 1 |
+| + | Alice | 1 |
+| + | Greg | 1 |
+| - | Bob | 1 |
+| + | Bob | 2 |
++-----+----------------------+----------------------+
+Received a total of 5 rows
+{% endhighlight %}
+
+如果这个查询以批的方式执行,显示的内容如下:
+{% highlight text %}
++-------+-----+
+| name | cnt |
++-------+-----+
+| Alice | 1 |
+| Bob | 2 |
+| Greg | 1 |
++-------+-----+
+3 rows in set
+{% endhighlight %}
+
+这几种结果模式在 SQL 查询的原型设计过程中都非常有用。这些模式的结果都存储在 SQL 客户端 的 Java 堆内存中。为了保持 CLI 界面及时响应,变更日志模式仅显示最近的 1000 个更改。表格模式支持浏览更大的结果,这些结果仅受可用主内存和配置的[最大行数](sqlClient.html#configuration)(`max-table-result-rows`)的限制。
-注意 在批处理环境下执行的查询只能用表格模式进行检索。
+注意 在批处理环境下执行的查询只能用表格模式或者Tableau模式进行检索。
定义查询语句后,可以将其作为长时间运行的独立 Flink 作业提交给集群。为此,其目标系统需要使用 [INSERT INTO 语句](sqlClient.html#detached-sql-queries)指定存储结果。[配置部分](sqlClient.html#configuration)解释如何声明读取数据的 table source,写入数据的 sink 以及配置其他表程序属性的方法。
@@ -257,16 +294,16 @@ tables:
type: csv
fields:
- name: MyField1
- type: INT
+ data-type: INT
- name: MyField2
- type: VARCHAR
+ data-type: VARCHAR
line-delimiter: "\n"
comment-prefix: "#"
schema:
- name: MyField1
- type: INT
+ data-type: INT
- name: MyField2
- type: VARCHAR
+ data-type: VARCHAR
- name: MyCustomView
type: view
query: "SELECT MyField2 FROM MyTableSource"
@@ -347,7 +384,7 @@ CLI commands > session environment file > defaults environment file
#### 重启策略(Restart Strategies)
-重启策略控制 Flink 作业失败时的重启方式。与 Flink 集群的[全局重启策略]({{ site.baseurl }}/zh/dev/restart_strategies.html)相似,更细精度的重启配置可以在环境配置文件中声明。
+重启策略控制 Flink 作业失败时的重启方式。与 Flink 集群的[全局重启策略]({{ site.baseurl }}/zh/dev/task_failure_recovery.html)相似,更细精度的重启配置可以在环境配置文件中声明。
Flink 支持以下策略:
@@ -600,7 +637,7 @@ Job ID: 6f922fe5cba87406ff23ae4a7bb79044
Web interface: http://localhost:8081
{% endhighlight %}
-注意 提交后,SQL 客户端不追踪正在运行的 Flink 作业状态。提交后可以关闭 CLI 进程,并且不会影响分离的查询。Flink 的[重启策略]({{ site.baseurl }}/zh/dev/restart_strategies.html)负责容错。取消查询可以用 Flink 的 web 接口、命令行或 REST API 。
+注意 提交后,SQL 客户端不追踪正在运行的 Flink 作业状态。提交后可以关闭 CLI 进程,并且不会影响分离的查询。Flink 的[重启策略]({{ site.baseurl }}/zh/dev/task_failure_recovery.html)负责容错。取消查询可以用 Flink 的 web 接口、命令行或 REST API 。
{% top %}
diff --git a/docs/dev/table/streaming/joins.md b/docs/dev/table/streaming/joins.md
index e621a2057dec05..98c96e6d5b3e46 100644
--- a/docs/dev/table/streaming/joins.md
+++ b/docs/dev/table/streaming/joins.md
@@ -48,10 +48,10 @@ These semantics allow for any kind of updating (insert, update, delete) input ta
However, this operation has an important implication: it requires to keep both sides of the join input in Flink's state forever.
Thus, the resource usage will grow indefinitely as well, if one or both input tables are continuously growing.
-Time-windowed Joins
+Interval Joins
-------------------
-A time-windowed join is defined by a join predicate, that checks if the [time attributes](time_attributes.html) of the input
+A interval join is defined by a join predicate, that checks if the [time attributes](time_attributes.html) of the input
records are within certain time constraints, i.e., a time window.
{% highlight sql %}
@@ -148,7 +148,7 @@ In our example, each record from `Orders` will be joined with the version of `Ra
In contrast to [regular joins](#regular-joins), this means that if there is a new record on the build side, it will not affect the previous results of the join.
This again allows Flink to limit the number of elements that must be kept in the state.
-Compared to [time-windowed joins](#time-windowed-joins), temporal table joins do not define a time window within which bounds the records will be joined.
+Compared to [interval joins](#interval-joins), temporal table joins do not define a time window within which bounds the records will be joined.
Records from the probe side are always joined with the build side's version at the time specified by the time attribute. Thus, records on the build side might be arbitrarily old.
As time passes, the previous and no longer needed versions of the record (for the given primary key) will be removed from the state.
@@ -304,7 +304,7 @@ Each record from the probe side will be joined with the current version of the b
In contrast to [regular joins](#regular-joins), the previous results of the temporal table join will not be affected despite the changes on the build side. Also, the temporal table join operator is very lightweight and does not keep any state.
-Compared to [time-windowed joins](#time-windowed-joins), temporal table joins do not define a time window within which the records will be joined.
+Compared to [interval joins](#interval-joins), temporal table joins do not define a time window within which the records will be joined.
Records from the probe side are always joined with the build side's latest version at processing time. Thus, records on the build side might be arbitrarily old.
Both [temporal table function join](#join-with-a-temporal-table-function) and temporal table join come from the same motivation but have different SQL syntax and runtime implementations:
diff --git a/docs/dev/table/streaming/joins.zh.md b/docs/dev/table/streaming/joins.zh.md
index e621a2057dec05..98c96e6d5b3e46 100644
--- a/docs/dev/table/streaming/joins.zh.md
+++ b/docs/dev/table/streaming/joins.zh.md
@@ -48,10 +48,10 @@ These semantics allow for any kind of updating (insert, update, delete) input ta
However, this operation has an important implication: it requires to keep both sides of the join input in Flink's state forever.
Thus, the resource usage will grow indefinitely as well, if one or both input tables are continuously growing.
-Time-windowed Joins
+Interval Joins
-------------------
-A time-windowed join is defined by a join predicate, that checks if the [time attributes](time_attributes.html) of the input
+A interval join is defined by a join predicate, that checks if the [time attributes](time_attributes.html) of the input
records are within certain time constraints, i.e., a time window.
{% highlight sql %}
@@ -148,7 +148,7 @@ In our example, each record from `Orders` will be joined with the version of `Ra
In contrast to [regular joins](#regular-joins), this means that if there is a new record on the build side, it will not affect the previous results of the join.
This again allows Flink to limit the number of elements that must be kept in the state.
-Compared to [time-windowed joins](#time-windowed-joins), temporal table joins do not define a time window within which bounds the records will be joined.
+Compared to [interval joins](#interval-joins), temporal table joins do not define a time window within which bounds the records will be joined.
Records from the probe side are always joined with the build side's version at the time specified by the time attribute. Thus, records on the build side might be arbitrarily old.
As time passes, the previous and no longer needed versions of the record (for the given primary key) will be removed from the state.
@@ -304,7 +304,7 @@ Each record from the probe side will be joined with the current version of the b
In contrast to [regular joins](#regular-joins), the previous results of the temporal table join will not be affected despite the changes on the build side. Also, the temporal table join operator is very lightweight and does not keep any state.
-Compared to [time-windowed joins](#time-windowed-joins), temporal table joins do not define a time window within which the records will be joined.
+Compared to [interval joins](#interval-joins), temporal table joins do not define a time window within which the records will be joined.
Records from the probe side are always joined with the build side's latest version at processing time. Thus, records on the build side might be arbitrarily old.
Both [temporal table function join](#join-with-a-temporal-table-function) and temporal table join come from the same motivation but have different SQL syntax and runtime implementations:
diff --git a/docs/dev/table/streaming/match_recognize.md b/docs/dev/table/streaming/match_recognize.md
index 8fb12fa5494c90..3fd20ecc777805 100644
--- a/docs/dev/table/streaming/match_recognize.md
+++ b/docs/dev/table/streaming/match_recognize.md
@@ -3,7 +3,6 @@ title: 'Detecting Patterns in Tables'
nav-parent_id: streaming_tableapi
nav-title: 'Detecting Patterns'
nav-pos: 5
-is_beta: true
---
It is a common use case to search for a set of event patterns, especially in case of data streams.
-Flink comes with a [complex event processing (CEP) library]({{ site.baseurl }}/dev/libs/cep.html)
+Flink comes with a [complex event processing (CEP) library]({% link dev/libs/cep.md %})
which allows for pattern detection in event streams. Furthermore, Flink's SQL API provides a
relational way of expressing queries with a large set of built-in functions and rule-based
optimizations that can be used out of the box.
@@ -67,9 +66,9 @@ FROM MyTable
This page will explain each keyword in more detail and will illustrate more complex examples.
-Attention Flink's implementation of the `MATCH_RECOGNIZE`
+{% info Notice %} Flink's implementation of the `MATCH_RECOGNIZE`
clause is a subset of the full standard. Only those features documented in the following sections
-are supported. Since the development is still in an early phase, please also take a look at the
+are supported. Additional features may be supported based on community feedback, please also take a look at the
[known limitations](#known-limitations).
* This will be replaced by the TOC
@@ -93,10 +92,10 @@ project.
{% endhighlight %}
Alternatively, you can also add the dependency to the cluster classpath (see the
-[dependency section]({{ site.baseurl}}/dev/projectsetup/dependencies.html) for more information).
+[dependency section]({% link dev/project-configuration.md %}) for more information).
If you want to use the `MATCH_RECOGNIZE` clause in the
-[SQL Client]({{ site.baseurl}}/dev/table/sqlClient.html), you don't have to do anything as all the
+[SQL Client]({% link dev/table/sqlClient.md %}), you don't have to do anything as all the
dependencies are included by default.
### SQL Semantics
@@ -263,8 +262,8 @@ look at the [event stream navigation](#pattern-navigation) section.
### Aggregations
Aggregations can be used in `DEFINE` and `MEASURES` clauses. Both
-[built-in]({{ site.baseurl }}/dev/table/functions/systemFunctions.html) and custom
-[user defined]({{ site.baseurl }}/dev/table/functions/udfs.html) functions are supported.
+[built-in]({% link dev/table/functions/systemFunctions.md %}) and custom
+[user defined]({% link dev/table/functions/udfs.md %}) functions are supported.
Aggregate functions are applied to each subset of rows mapped to a match. In order to understand
how those subsets are evaluated have a look at the [event stream navigation](#pattern-navigation)
@@ -1038,7 +1037,7 @@ use [time attributes](time_attributes.html). To select those there are available
Returns the timestamp of the last row that was mapped to the given pattern.
diff --git a/docs/dev/table/streaming/match_recognize.zh.md b/docs/dev/table/streaming/match_recognize.zh.md
index 8fb12fa5494c90..54091d415dcd31 100644
--- a/docs/dev/table/streaming/match_recognize.zh.md
+++ b/docs/dev/table/streaming/match_recognize.zh.md
@@ -3,7 +3,6 @@ title: 'Detecting Patterns in Tables'
nav-parent_id: streaming_tableapi
nav-title: 'Detecting Patterns'
nav-pos: 5
-is_beta: true
---
-Temporal Tables represent a concept of a (parameterized) view on a changing table that returns the content of a table at a specific point in time.
+时态表(Temporal Table)代表基于表的(参数化)视图概念,该表记录变更历史,该视图返回表在某个特定时间点的内容。
-The changing table can either be a changing history table which tracks the changes (e.g. database changelogs) or a changing dimension table which materializes the changes (e.g. database tables).
+变更表可以是跟踪变化的历史记录表(例如数据库变更日志),也可以是有具体更改的维表(例如数据库表)。
-For the changing history table, Flink can keep track of the changes and allows for accessing the content of the table at a certain point in time within a query. In Flink, this kind of table is represented by a *Temporal Table Function*.
+对于记录变更历史的表,Flink 可以追踪这些变化,并且允许查询这张表在某个特定时间点的内容。在 Flink 中,这类表由*时态表函数(Temporal Table Function)*表示。
-For the changing dimension table, Flink allows for accessing the content of the table at processing time within a query. In Flink, this kind of table is represented by a *Temporal Table*.
+对于变化的维表,Flink 允许查询这张表在处理时的内容,在 Flink 中,此类表由*时态表(Temporal Table)*表示。
* This will be replaced by the TOC
{:toc}
-Motivation
+设计初衷
----------
-### Correlate with a changing history table
+### 与记录变更历史的表相关
-Let's assume that we have the following table `RatesHistory`.
+假设我们有表 `RatesHistory` 如下所示。
{% highlight sql %}
SELECT * FROM RatesHistory;
@@ -53,10 +53,9 @@ rowtime currency rate
11:49 Pounds 108
{% endhighlight %}
-`RatesHistory` represents an ever growing append-only table of currency exchange rates with respect to `Yen` (which has a rate of `1`).
-For example, the exchange rate for the period from `09:00` to `10:45` of `Euro` to `Yen` was `114`. From `10:45` to `11:15` it was `116`.
+`RatesHistory` 代表一个兑换日元货币汇率表(日元汇率为1),该表是不断增长的 append-only 表。例如,`欧元`兑`日元`从 `09:00` 到 `10:45` 的汇率为 `114`。从 `10:45` 到 `11:15`,汇率为 `116`。
-Given that we would like to output all current rates at the time `10:58`, we would need the following SQL query to compute a result table:
+假设我们要输出 `10:58` 的所有当前汇率,则需要以下 SQL 查询来计算结果表:
{% highlight sql %}
SELECT *
@@ -68,9 +67,9 @@ WHERE r.rowtime = (
AND r2.rowtime <= TIME '10:58');
{% endhighlight %}
-The correlated subquery determines the maximum time for the corresponding currency that is lower or equal than the desired time. The outer query lists the rates that have a maximum timestamp.
-
-The following table shows the result of such a computation. In our example, the update to `Euro` at `10:45` is taken into account, however, the update to `Euro` at `11:15` and the new entry of `Pounds` are not considered in the table's version at time `10:58`.
+子查询确定对应货币的最大时间小于或等于所需时间。外部查询列出具有最大时间戳的汇率。
+
+下表显示了这种计算的结果。我们的示例中,在 `10:58` 时表的内容,考虑了 `10:45` 时`欧元`的更新,但未考虑 `11:15` 时的`欧元`更新和`英镑`的新值。
{% highlight text %}
rowtime currency rate
@@ -80,17 +79,17 @@ rowtime currency rate
10:45 Euro 116
{% endhighlight %}
-The concept of *Temporal Tables* aims to simplify such queries, speed up their execution, and reduce Flink's state usage. A *Temporal Table* is a parameterized view on an append-only table that interprets the rows of the append-only table as the changelog of a table and provides the version of that table at a specific point in time. Interpreting the append-only table as a changelog requires the specification of a primary key attribute and a timestamp attribute. The primary key determines which rows are overwritten and the timestamp determines the time during which a row is valid.
+ *时态表*的概念旨在简化此类查询,加快其执行速度,并减少 Flink 的状态使用。*时态表*是 append-only 表上的参数化视图,该视图将 append-only 表的行解释为表的变更日志,并在特定时间点提供该表的版本。将 append-only 表解释为变更日志需要指定主键属性和时间戳属性。主键确定哪些行将被覆盖,时间戳确定行有效的时间。
-In the above example `currency` would be a primary key for `RatesHistory` table and `rowtime` would be the timestamp attribute.
+在上面的示例中,`currency` 是 `RatesHistory` 表的主键,而 `rowtime` 是时间戳属性。
-In Flink, this is represented by a [*Temporal Table Function*](#temporal-table-function).
+在 Flink 中,这由[*时态表函数*](#temporal-table-function)表示。
-### Correlate with a changing dimension table
+### 与维表变化相关
-On the other hand, some use cases require to join a changing dimension table which is an external database table.
+另一方面,某些用例需要连接变化的维表,该表是外部数据库表。
-Let's assume that `LatestRates` is a table (e.g. stored in) which is materialized with the latest rate. The `LatestRates` is the materialized history `RatesHistory`. Then the content of `LatestRates` table at time `10:58` will be:
+假设 `LatestRates` 是一个被物化的最新汇率表。`LatestRates` 是物化的 `RatesHistory` 历史。那么 `LatestRates` 表在 `10:58` 的内容将是:
{% highlight text %}
10:58> SELECT * FROM LatestRates;
@@ -101,7 +100,7 @@ Yen 1
Euro 116
{% endhighlight %}
-The content of `LatestRates` table at time `12:00` will be:
+`12:00` 时 `LatestRates` 表的内容将是:
{% highlight text %}
12:00> SELECT * FROM LatestRates;
@@ -113,18 +112,20 @@ Euro 119
Pounds 108
{% endhighlight %}
-In Flink, this is represented by a [*Temporal Table*](#temporal-table).
+在 Flink 中,这由[*时态表*](#temporal-table)表示。
+
+
-Temporal Table Function
+时态表函数
------------------------
-In order to access the data in a temporal table, one must pass a [time attribute](time_attributes.html) that determines the version of the table that will be returned.
-Flink uses the SQL syntax of [table functions]({{ site.baseurl }}/dev/table/functions/udfs.html#table-functions) to provide a way to express it.
+为了访问时态表中的数据,必须传递一个[时间属性](time_attributes.html),该属性确定将要返回的表的版本。
+Flink 使用[表函数]({{ site.baseurl }}/zh/dev/table/functions/udfs.html#table-functions)的 SQL 语法提供一种表达它的方法。
-Once defined, a *Temporal Table Function* takes a single time argument `timeAttribute` and returns a set of rows.
-This set contains the latest versions of the rows for all of the existing primary keys with respect to the given time attribute.
+定义后,*时态表函数*将使用单个时间参数 timeAttribute 并返回一个行集合。
+该集合包含相对于给定时间属性的所有现有主键的行的最新版本。
-Assuming that we defined a temporal table function `Rates(timeAttribute)` based on `RatesHistory` table, we could query such a function in the following way:
+假设我们基于 `RatesHistory` 表定义了一个时态表函数,我们可以通过以下方式查询该函数 `Rates(timeAttribute)`:
{% highlight sql %}
SELECT * FROM Rates('10:15');
@@ -144,16 +145,15 @@ rowtime currency rate
09:00 Yen 1
{% endhighlight %}
-Each query to `Rates(timeAttribute)` would return the state of the `Rates` for the given `timeAttribute`.
+对 `Rates(timeAttribute)` 的每个查询都将返回给定 `timeAttribute` 的 `Rates` 状态。
-**Note**: Currently, Flink doesn't support directly querying the temporal table functions with a constant time attribute parameter. At the moment, temporal table functions can only be used in joins.
-The example above was used to provide an intuition about what the function `Rates(timeAttribute)` returns.
+**注意**:当前 Flink 不支持使用常量时间属性参数直接查询时态表函数。目前,时态表函数只能在 join 中使用。上面的示例用于为函数 `Rates(timeAttribute)` 返回内容提供直观信息。
-See also the page about [joins for continuous queries](joins.html) for more information about how to join with a temporal table.
+另请参阅有关[用于持续查询的 join ](joins.html)页面,以获取有关如何与时态表 join 的更多信息。
-### Defining Temporal Table Function
+### 定义时态表函数
-The following code snippet illustrates how to create a temporal table function from an append-only table.
+以下代码段说明了如何从 append-only 表中创建时态表函数。
@@ -161,11 +161,11 @@ The following code snippet illustrates how to create a temporal table function f
import org.apache.flink.table.functions.TemporalTableFunction;
(...)
-// Get the stream and table environments.
+// 获取 stream 和 table 环境
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
StreamTableEnvironment tEnv = StreamTableEnvironment.create(env);
-// Provide a static data set of the rates history table.
+// 提供一个汇率历史记录表静态数据集
List> ratesHistoryData = new ArrayList<>();
ratesHistoryData.add(Tuple2.of("US Dollar", 102L));
ratesHistoryData.add(Tuple2.of("Euro", 114L));
@@ -173,26 +173,26 @@ ratesHistoryData.add(Tuple2.of("Yen", 1L));
ratesHistoryData.add(Tuple2.of("Euro", 116L));
ratesHistoryData.add(Tuple2.of("Euro", 119L));
-// Create and register an example table using above data set.
-// In the real setup, you should replace this with your own table.
+// 用上面的数据集创建并注册一个示例表
+// 在实际设置中,应使用自己的表替换它
DataStream> ratesHistoryStream = env.fromCollection(ratesHistoryData);
Table ratesHistory = tEnv.fromDataStream(ratesHistoryStream, $("r_currency"), $("r_rate"), $("r_proctime").proctime());
tEnv.createTemporaryView("RatesHistory", ratesHistory);
-// Create and register a temporal table function.
-// Define "r_proctime" as the time attribute and "r_currency" as the primary key.
+// 创建和注册时态表函数
+// 指定 "r_proctime" 为时间属性,指定 "r_currency" 为主键
TemporalTableFunction rates = ratesHistory.createTemporalTableFunction("r_proctime", "r_currency"); // <==== (1)
tEnv.registerFunction("Rates", rates); // <==== (2)
{% endhighlight %}
{% highlight scala %}
-// Get the stream and table environments.
+// 获取 stream 和 table 环境
val env = StreamExecutionEnvironment.getExecutionEnvironment
val tEnv = StreamTableEnvironment.create(env)
-// Provide a static data set of the rates history table.
+// 提供一个汇率历史记录表静态数据集
val ratesHistoryData = new mutable.MutableList[(String, Long)]
ratesHistoryData.+=(("US Dollar", 102L))
ratesHistoryData.+=(("Euro", 114L))
@@ -200,35 +200,36 @@ ratesHistoryData.+=(("Yen", 1L))
ratesHistoryData.+=(("Euro", 116L))
ratesHistoryData.+=(("Euro", 119L))
-// Create and register an example table using above data set.
-// In the real setup, you should replace this with your own table.
+// 用上面的数据集创建并注册一个示例表
+// 在实际设置中,应使用自己的表替换它
val ratesHistory = env
.fromCollection(ratesHistoryData)
.toTable(tEnv, 'r_currency, 'r_rate, 'r_proctime.proctime)
tEnv.createTemporaryView("RatesHistory", ratesHistory)
-// Create and register TemporalTableFunction.
-// Define "r_proctime" as the time attribute and "r_currency" as the primary key.
+// 创建和注册时态表函数
+// 指定 "r_proctime" 为时间属性,指定 "r_currency" 为主键
val rates = ratesHistory.createTemporalTableFunction($"r_proctime", $"r_currency") // <==== (1)
tEnv.registerFunction("Rates", rates) // <==== (2)
{% endhighlight %}
-Line `(1)` creates a `rates` [temporal table function](#temporal-table-functions),
-which allows us to use the function `rates` in the [Table API](../tableApi.html#joins).
+行`(1)`创建了一个 `rates` [时态表函数](#temporal-table-function),
+这使我们可以在[ Table API ](../tableApi.html#joins)中使用 `rates` 函数。
-Line `(2)` registers this function under the name `Rates` in our table environment,
-which allows us to use the `Rates` function in [SQL]({{ site.baseurl }}/dev/table/sql/queries.html#joins).
+行`(2)`在表环境中注册名称为 `Rates` 的函数,这使我们可以在[ SQL ]({{ site.baseurl }}/zh/dev/table/sql/queries.html#joins)中使用 `Rates` 函数。
-## Temporal Table
+
-Attention This is only supported in Blink planner.
+## 时态表
-In order to access data in temporal table, currently one must define a `TableSource` with `LookupableTableSource`. Flink uses the SQL syntax of `FOR SYSTEM_TIME AS OF` to query temporal table, which is proposed in SQL:2011.
+注意 仅 Blink planner 支持此功能。
-Assuming that we defined a temporal table called `LatestRates`, we can query such a table in the following way:
+为了访问时态表中的数据,当前必须使用 `LookupableTableSource` 定义一个 `TableSource`。Flink 使用 SQL:2011 中提出的 `FOR SYSTEM_TIME AS OF` 的 SQL 语法查询时态表。
+
+假设我们定义了一个时态表 `LatestRates`,我们可以通过以下方式查询此表:
{% highlight sql %}
SELECT * FROM LatestRates FOR SYSTEM_TIME AS OF TIME '10:15';
@@ -248,48 +249,60 @@ Euro 116
Yen 1
{% endhighlight %}
-**Note**: Currently, Flink doesn't support directly querying the temporal table with a constant time. At the moment, temporal table can only be used in joins. The example above is used to provide an intuition about what the temporal table `LatestRates` returns.
+**注意**:当前,Flink 不支持以固定时间直接查询时态表。目前,时态表只能在 join 中使用。上面的示例用于为时态表 `LatestRates` 返回内容提供直观信息。
-See also the page about [joins for continuous queries](joins.html) for more information about how to join with a temporal table.
+另请参阅有关[用于持续查询的 join ](joins.html)页面,以获取有关如何与时态表 join 的更多信息。
-### Defining Temporal Table
+### 定义时态表
{% highlight java %}
-// Get the stream and table environments.
+// 获取 stream 和 table 环境
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
-StreamTableEnvironment tEnv = TableEnvironment.getTableEnvironment(env);
-
-// Create an HBaseTableSource as a temporal table which implements LookableTableSource
-// In the real setup, you should replace this with your own table.
-HBaseTableSource rates = new HBaseTableSource(conf, "Rates");
-rates.setRowKey("currency", String.class); // currency as the primary key
-rates.addColumn("fam1", "rate", Double.class);
-
-// register the temporal table into environment, then we can query it in sql
-tEnv.registerTableSource("Rates", rates);
+EnvironmentSettings settings = EnvironmentSettings.newInstance().build();
+StreamTableEnvironment tEnv = StreamTableEnvironment.create(env, settings);
+// or TableEnvironment tEnv = TableEnvironment.create(settings);
+
+// 用 DDL 定义一张 HBase 表,然后我们可以在 SQL 中将其当作一张时态表使用
+// 'currency' 列是 HBase 表中的 rowKey
+tEnv.executeSql(
+ "CREATE TABLE LatestRates (" +
+ " currency STRING," +
+ " fam1 ROW" +
+ ") WITH (" +
+ " 'connector' = 'hbase-1.4'," +
+ " 'table-name' = 'Rates'," +
+ " 'zookeeper.quorum' = 'localhost:2181'" +
+ ")");
{% endhighlight %}
{% highlight scala %}
-// Get the stream and table environments.
+// 获取 stream 和 table 环境
val env = StreamExecutionEnvironment.getExecutionEnvironment
-val tEnv = TableEnvironment.getTableEnvironment(env)
-
-// Create an HBaseTableSource as a temporal table which implements LookableTableSource
-// In the real setup, you should replace this with your own table.
-val rates = new HBaseTableSource(conf, "Rates")
-rates.setRowKey("currency", String.class) // currency as the primary key
-rates.addColumn("fam1", "rate", Double.class)
-
-// register the temporal table into environment, then we can query it in sql
-tEnv.registerTableSource("Rates", rates)
+val settings = EnvironmentSettings.newInstance().build()
+val tEnv = StreamTableEnvironment.create(env, settings)
+// or val tEnv = TableEnvironment.create(settings)
+
+// 用 DDL 定义一张 HBase 表,然后我们可以在 SQL 中将其当作一张时态表使用
+// 'currency' 列是 HBase 表中的 rowKey
+tEnv.executeSql(
+ s"""
+ |CREATE TABLE LatestRates (
+ | currency STRING,
+ | fam1 ROW
+ |) WITH (
+ | 'connector' = 'hbase-1.4',
+ | 'table-name' = 'Rates',
+ | 'zookeeper.quorum' = 'localhost:2181'
+ |)
+ |""".stripMargin)
{% endhighlight %}
-See also the page about [how to define LookupableTableSource](../sourceSinks.html#defining-a-tablesource-for-lookups).
+另请参阅有关[如何定义 LookupableTableSource ](../sourceSinks.html#defining-a-tablesource-for-lookups)的页面。
{% top %}
diff --git a/docs/dev/table/streaming/time_attributes.md b/docs/dev/table/streaming/time_attributes.md
index a94d0bdfb6a38b..6896007ea58255 100644
--- a/docs/dev/table/streaming/time_attributes.md
+++ b/docs/dev/table/streaming/time_attributes.md
@@ -120,10 +120,10 @@ The processing time attribute is defined with the `.proctime` property during sc
DataStream> stream = ...;
// declare an additional logical field as a processing time attribute
-Table table = tEnv.fromDataStream(stream, $("user_name"), $("data"), $("user_action_time").proctime()");
+Table table = tEnv.fromDataStream(stream, $("user_name"), $("data"), $("user_action_time").proctime());
WindowedTable windowedTable = table.window(
- Tumble.over(interval(Duration.ofMinutes(10)))
+ Tumble.over(lit(10).minutes())
.on($("user_action_time"))
.as("userActionWindow"));
{% endhighlight %}
@@ -177,7 +177,7 @@ tEnv.registerTableSource("user_actions", new UserActionSource());
WindowedTable windowedTable = tEnv
.from("user_actions")
.window(Tumble
- .over(interval(Duration.ofMinutes(10)))
+ .over(lit(10).minutes())
.on($("user_action_time"))
.as("userActionWindow"));
{% endhighlight %}
@@ -285,7 +285,7 @@ Table table = tEnv.fromDataStream(stream, $("user_action_time").rowtime(), $("us
// Usage:
WindowedTable windowedTable = table.window(Tumble
- .over(interval(Duration.ofMinutes(10)))
+ .over(lit(10).minutes())
.on($("user_action_time"))
.as("userActionWindow"));
{% endhighlight %}
@@ -368,7 +368,7 @@ tEnv.registerTableSource("user_actions", new UserActionSource());
WindowedTable windowedTable = tEnv
.from("user_actions")
- .window(Tumble.over(interval(Duration.ofMinutes(10))).on($("user_action_time")).as("userActionWindow"));
+ .window(Tumble.over(lit(10).minutes()).on($("user_action_time")).as("userActionWindow"));
{% endhighlight %}
diff --git a/docs/dev/table/tableApi.md b/docs/dev/table/tableApi.md
index 71977bddc1415e..1fc323df02bf2e 100644
--- a/docs/dev/table/tableApi.md
+++ b/docs/dev/table/tableApi.md
@@ -46,7 +46,6 @@ For the Expression DSL it is also necessary to import static `org.apache.flink.t
{% highlight java %}
import org.apache.flink.table.api.*
-import org.apache.flink.table.api.java.*
import static org.apache.flink.table.api.Expressions.*
@@ -73,14 +72,14 @@ result.print();
-The Scala Table API is enabled by importing `org.apache.flink.api.scala._` and `org.apache.flink.table.api.scala._`.
+The Scala Table API is enabled by importing `org.apache.flink.table.api._`, `org.apache.flink.api.scala._`, and `org.apache.flink.table.api.bridge.scala._` (for bridging to/from DataStream).
The following example shows how a Scala Table API program is constructed. Table fields are referenced using Scala's String interpolation using a dollar character (`$`).
{% highlight scala %}
import org.apache.flink.api.scala._
import org.apache.flink.table.api._
-import org.apache.flink.table.api.scala._
+import org.apache.flink.table.api.bridge.scala._
// environment configuration
val env = ExecutionEnvironment.getExecutionEnvironment
@@ -149,7 +148,7 @@ Table result = orders
$("c").isNotNull()
))
.select($("a").lowerCase().as("a"), $("b"), $("rowtime"))
- .window(Tumble.over(interval(Duration.ofHours(1))).on($("rowtime")).as("hourlyWindow"))
+ .window(Tumble.over(lit(1).hours()).on($("rowtime")).as("hourlyWindow"))
.groupBy($("hourlyWindow"), $("a"))
.select($("a"), $("hourlyWindow").end().as("hour"), $("b").avg().as("avgBillingAmount"));
{% endhighlight %}
@@ -228,6 +227,50 @@ Table orders = tableEnv.from("Orders");
{% endhighlight %}
+
+
+ Values
+ BatchStreaming
+
+
+
Similar to the VALUES clause in a SQL query. Produces an inline table out of the provided rows.
+
You can use a `row(...)` expression to create composite rows:
+{% highlight text %}
+root
+|-- f0: BIGINT NOT NULL // original types INT and BIGINT are generalized to BIGINT
+|-- f1: VARCHAR(5) NOT NULL // original types CHAR(3) and CHAR(5) are generalized
+ // to VARCHAR(5). VARCHAR is used instead of CHAR so that
+ // no padding is applied
+{% endhighlight %}
+
The method will derive the types automatically from the input expressions. If types
+ at a certain position differ, the method will try to find a common super type for all types. If a common
+ super type does not exist, an exception will be thrown.
+
You can also specify the requested type explicitly. It might be helpful for assigning more generic types like e.g. DECIMAL or naming the columns.
+{% highlight text %}
+root
+|-- f0: BIGINT NOT NULL // original types INT and BIGINT are generalized to BIGINT
+|-- f1: VARCHAR(5) NOT NULL // original types CHAR(3) and CHAR(5) are generalized
+ // to VARCHAR(5). VARCHAR is used instead of CHAR so that
+ // no padding is applied
+{% endhighlight %}
+
The method will derive the types automatically from the input expressions. If types
+ at a certain position differ, the method will try to find a common super type for all types. If a common
+ super type does not exist, an exception will be thrown.
+
You can also specify the requested type explicitly. It might be helpful for assigning more generic types like e.g. DECIMAL or naming the columns.
Select BatchStreaming
@@ -677,7 +764,7 @@ Table result = orders.groupBy($("a")).select($("a"), $("b").sum().as("d"));
{% highlight java %}
Table orders = tableEnv.from("Orders");
Table result = orders
- .window(Tumble.over(interval(Duration.ofMinutes(5))).on($("rowtime")).as("w")) // define window
+ .window(Tumble.over(lit(5).minutes())).on($("rowtime")).as("w")) // define window
.groupBy($("a"), $("w")) // group by key and window
// access window properties and aggregate
.select(
@@ -736,7 +823,7 @@ Table groupByDistinctResult = orders
// Distinct aggregation on time window group by
Table groupByWindowDistinctResult = orders
.window(Tumble
- .over(interval(Duration.ofMinutes(5)))
+ .over(lit(5).minutes()))
.on($("rowtime"))
.as("w")
)
@@ -1075,15 +1162,15 @@ Table fullOuterResult = left.fullOuterJoin(right, $("a").isEqual($("d")))
-
Time-windowed Join
+
Interval Join BatchStreaming
-
Note: Time-windowed joins are a subset of regular joins that can be processed in a streaming fashion.
+
Note: Interval joins are a subset of regular joins that can be processed in a streaming fashion.
-
A time-windowed join requires at least one equi-join predicate and a join condition that bounds the time on both sides. Such a condition can be defined by two appropriate range predicates (<, <=, >=, >) or a single equality predicate that compares time attributes of the same type (i.e., processing time or event time) of both input tables.
-
For example, the following predicates are valid window join conditions:
+
An interval join requires at least one equi-join predicate and a join condition that bounds the time on both sides. Such a condition can be defined by two appropriate range predicates (<, <=, >=, >) or a single equality predicate that compares time attributes of the same type (i.e., processing time or event time) of both input tables.
+
For example, the following predicates are valid interval join conditions:
Note: Time-windowed joins are a subset of regular joins that can be processed in a streaming fashion.
+
Note: Interval joins are a subset of regular joins that can be processed in a streaming fashion.
-
A time-windowed join requires at least one equi-join predicate and a join condition that bounds the time on both sides. Such a condition can be defined by two appropriate range predicates (<, <=, >=, >) or a single equality predicate that compares time attributes of the same type (i.e., processing time or event time) of both input tables.
-
For example, the following predicates are valid window join conditions:
+
An interval join requires at least one equi-join predicate and a join condition that bounds the time on both sides. Such a condition can be defined by two appropriate range predicates (<, <=, >=, >) or a single equality predicate that compares time attributes of the same type (i.e., processing time or event time) of both input tables.
+
For example, the following predicates are valid interval join conditions:
Note: Interval joins are a subset of regular joins that can be processed in a streaming fashion.
+
+
An interval join requires at least one equi-join predicate and a join condition that bounds the time on both sides. Such a condition can be defined by two appropriate range predicates (<, <=, >=, >) or a single equality predicate that compares time attributes of the same type (i.e., processing time or event time) of both input tables.
+
For example, the following predicates are valid interval join conditions:
Similar to the INSERT INTO clause in a SQL query. Performs a insertion into a registered output table.
+
Similar to the `INSERT INTO` clause in a SQL query, the method performs an insertion into a registered output table. The `executeInsert()` method will immediately submit a Flink job which execute the insert operation.
Output tables must be registered in the TableEnvironment (see Connector tables). Moreover, the schema of the registered table must match the schema of the query.
Similar to the INSERT INTO clause in a SQL query. Performs a insertion into a registered output table.
+
Similar to the `INSERT INTO` clause in a SQL query, the method performs an insertion into a registered output table. The `executeInsert()` method will immediately submit a Flink job which execute the insert operation.
Output tables must be registered in the TableEnvironment (see Connector tables). Moreover, the schema of the registered table must match the schema of the query.
Similar to the INSERT INTO clause in a SQL query. Performs a insertion into a registered output table.
+
Similar to the INSERT INTO clause in a SQL query. Performs a insertion into a registered output table. The executeInsert method will immediately submit a flink job which execute the insert operation.
Output tables must be registered in the TableEnvironment (see Register a TableSink). Moreover, the schema of the registered table must match the schema of the query.
@@ -2186,10 +2291,10 @@ Tumbling windows are defined by using the `Tumble` class as follows:
{% highlight java %}
// Tumbling Event-time Window
-.window(Tumble.over(interval(Duration.ofMinutes(10))).on($("rowtime")).as("w"));
+.window(Tumble.over(lit(10).minutes()).on($("rowtime")).as("w"));
// Tumbling Processing-time Window (assuming a processing-time attribute "proctime")
-.window(Tumble.over(interval(Duration.ofMinutes(10))).on($("proctime")).as("w"));
+.window(Tumble.over(lit(10).minutes()).on($("proctime")).as("w"));
// Tumbling Row-count Window (assuming a processing-time attribute "proctime")
.window(Tumble.over(rowInterval(10)).on($("proctime")).as("w"));
@@ -2261,14 +2366,14 @@ Sliding windows are defined by using the `Slide` class as follows:
{% highlight java %}
// Sliding Event-time Window
-.window(Slide.over(interval(Duration.ofMinutes(10)))
- .every(interval(Duration.ofMinutes(5)))
+.window(Slide.over(lit(10).minutes())
+ .every(lit(5).minutes())
.on($("rowtime"))
.as("w"));
// Sliding Processing-time window (assuming a processing-time attribute "proctime")
-.window(Slide.over(interval(Duration.ofMinutes(10)))
- .every(interval(Duration.ofMinutes(5)))
+.window(Slide.over(lit(10).minutes())
+ .every(lit(5).minutes())
.on($("proctime"))
.as("w"));
@@ -2338,10 +2443,10 @@ A session window is defined by using the `Session` class as follows:
@@ -2527,10 +2632,10 @@ The `OverWindow` defines a range of rows over which aggregates are computed. `Ov
{% highlight java %}
// Bounded Event-time over window (assuming an event-time attribute "rowtime")
-.window(Over.partitionBy($("a")).orderBy($("rowtime")).preceding(interval(Duration.ofMinutes(1))).as("w"))
+.window(Over.partitionBy($("a")).orderBy($("rowtime")).preceding(lit(1).minutes()).as("w"))
// Bounded Processing-time over window (assuming a processing-time attribute "proctime")
-.window(Over.partitionBy($("a")).orderBy($("proctime")).preceding(interval(Duration.ofMinutes(1))).as("w"))
+.window(Over.partitionBy($("a")).orderBy($("proctime")).preceding(lit(1).minutes()).as("w"))
// Bounded Event-time Row-count over window (assuming an event-time attribute "rowtime")
.window(Over.partitionBy($("a")).orderBy($("rowtime")).preceding(rowInterval(10)).as("w"))
@@ -2718,7 +2823,7 @@ AggregateFunction myAggFunc = new MyMinMax();
tableEnv.registerFunction("myAggFunc", myAggFunc);
Table table = input
- .window(Tumble.over(interval(Duration.ofMinutes(5)))
+ .window(Tumble.over(lit(5).minutes())
.on($("rowtime"))
.as("w")) // define window
.groupBy($("key"), $("w")) // group by key and window
@@ -2810,7 +2915,7 @@ Table result = orders
tableEnv.registerFunction("top2", new Top2());
Table orders = tableEnv.from("Orders");
Table result = orders
- .window(Tumble.over(interval(Duration.ofMinutes(5)))
+ .window(Tumble.over(lit(5).minutes())
.on($("rowtime"))
.as("w")) // define window
.groupBy($("a"), $("w")) // group by key and window
@@ -3158,6 +3263,6 @@ timeIndicator = fieldReference , "." , ( "proctime" | "rowtime" ) ;
**Temporal intervals:** Temporal intervals can be represented as number of months (`Types.INTERVAL_MONTHS`) or number of milliseconds (`Types.INTERVAL_MILLIS`). Intervals of same type can be added or subtracted (e.g. `1.hour + 10.minutes`). Intervals of milliseconds can be added to time points (e.g. `"2016-08-10".toDate + 5.days`).
-**Scala expressions:** Scala expressions use implicit conversions. Therefore, make sure to add the wildcard import `org.apache.flink.table.api.scala._` to your programs. In case a literal is not treated as an expression, use `.toExpr` such as `3.toExpr` to force a literal to be converted.
+**Scala expressions:** Scala expressions use implicit conversions. Therefore, make sure to add the wildcard import `org.apache.flink.table.api._` to your programs. In case a literal is not treated as an expression, use `.toExpr` such as `3.toExpr` to force a literal to be converted.
{% top %}
diff --git a/docs/dev/table/tableApi.zh.md b/docs/dev/table/tableApi.zh.md
index 25e2904ae414ec..50fc7287566c71 100644
--- a/docs/dev/table/tableApi.zh.md
+++ b/docs/dev/table/tableApi.zh.md
@@ -46,7 +46,6 @@ For the Expression DSL it is also necessary to import static `org.apache.flink.t
{% highlight java %}
import org.apache.flink.table.api.*
-import org.apache.flink.table.api.java.*
import static org.apache.flink.table.api.Expressions.*
@@ -73,14 +72,14 @@ result.print();
-The Scala Table API is enabled by importing `org.apache.flink.api.scala._` and `org.apache.flink.table.api.scala._`.
+The Scala Table API is enabled by importing `org.apache.flink.table.api._`, `org.apache.flink.api.scala._`, and `org.apache.flink.table.api.bridge.scala._` (for bridging to/from DataStream).
The following example shows how a Scala Table API program is constructed. Table fields are referenced using Scala's String interpolation using a dollar character (`$`).
{% highlight scala %}
import org.apache.flink.api.scala._
import org.apache.flink.table.api._
-import org.apache.flink.table.api.scala._
+import org.apache.flink.table.api.bridge.scala._
// environment configuration
val env = ExecutionEnvironment.getExecutionEnvironment
@@ -149,7 +148,7 @@ Table result = orders
$("c").isNotNull()
))
.select($("a").lowerCase().as("a"), $("b"), $("rowtime"))
- .window(Tumble.over(interval(Duration.ofHours(1))).on($("rowtime")).as("hourlyWindow"))
+ .window(Tumble.over(lit(1).hours()).on($("rowtime")).as("hourlyWindow"))
.groupBy($("hourlyWindow"), $("a"))
.select($("a"), $("hourlyWindow").end().as("hour"), $("b").avg().as("avgBillingAmount"));
{% endhighlight %}
@@ -228,6 +227,50 @@ Table orders = tableEnv.from("Orders");
{% endhighlight %}
+
+
+ Values
+ BatchStreaming
+
+
+
Similar to the VALUES clause in a SQL query. Produces an inline table out of the provided rows.
+
You can use a `row(...)` expression to create composite rows:
+{% highlight text %}
+root
+|-- f0: BIGINT NOT NULL // original types INT and BIGINT are generalized to BIGINT
+|-- f1: VARCHAR(5) NOT NULL // original types CHAR(3) and CHAR(5) are generalized
+ // to VARCHAR(5). VARCHAR is used instead of CHAR so that
+ // no padding is applied
+{% endhighlight %}
+
The method will derive the types automatically from the input expressions. If types
+ at a certain position differ, the method will try to find a common super type for all types. If a common
+ super type does not exist, an exception will be thrown.
+
You can also specify the requested type explicitly. It might be helpful for assigning more generic types like e.g. DECIMAL or naming the columns.
+{% highlight text %}
+root
+|-- f0: BIGINT NOT NULL // original types INT and BIGINT are generalized to BIGINT
+|-- f1: VARCHAR(5) NOT NULL // original types CHAR(3) and CHAR(5) are generalized
+ // to VARCHAR(5). VARCHAR is used instead of CHAR so that
+ // no padding is applied
+{% endhighlight %}
+
The method will derive the types automatically from the input expressions. If types
+ at a certain position differ, the method will try to find a common super type for all types. If a common
+ super type does not exist, an exception will be thrown.
+
You can also specify the requested type explicitly. It might be helpful for assigning more generic types like e.g. DECIMAL or naming the columns.
Select BatchStreaming
@@ -677,7 +764,7 @@ Table result = orders.groupBy($("a")).select($("a"), $("b").sum().as("d"));
{% highlight java %}
Table orders = tableEnv.from("Orders");
Table result = orders
- .window(Tumble.over(interval(Duration.ofMinutes(5))).on($("rowtime")).as("w")) // define window
+ .window(Tumble.over(lit(5).minutes()).on($("rowtime")).as("w")) // define window
.groupBy($("a"), $("w")) // group by key and window
// access window properties and aggregate
.select(
@@ -736,7 +823,7 @@ Table groupByDistinctResult = orders
// Distinct aggregation on time window group by
Table groupByWindowDistinctResult = orders
.window(Tumble
- .over(interval(Duration.ofMinutes(5)))
+ .over(lit(5).minutes())
.on($("rowtime"))
.as("w")
)
@@ -1074,15 +1161,15 @@ Table fullOuterResult = left.fullOuterJoin(right, $("a").isEqual($("d")))
-
Time-windowed Join
+
Interval Join BatchStreaming
-
Note: Time-windowed joins are a subset of regular joins that can be processed in a streaming fashion.
+
Note: Interval joins are a subset of regular joins that can be processed in a streaming fashion.
-
A time-windowed join requires at least one equi-join predicate and a join condition that bounds the time on both sides. Such a condition can be defined by two appropriate range predicates (<, <=, >=, >) or a single equality predicate that compares time attributes of the same type (i.e., processing time or event time) of both input tables.
-
For example, the following predicates are valid window join conditions:
+
A interval join requires at least one equi-join predicate and a join condition that bounds the time on both sides. Such a condition can be defined by two appropriate range predicates (<, <=, >=, >) or a single equality predicate that compares time attributes of the same type (i.e., processing time or event time) of both input tables.
+
For example, the following predicates are valid interval join conditions:
Note: Time-windowed joins are a subset of regular joins that can be processed in a streaming fashion.
+
Note: Interval joins are a subset of regular joins that can be processed in a streaming fashion.
-
A time-windowed join requires at least one equi-join predicate and a join condition that bounds the time on both sides. Such a condition can be defined by two appropriate range predicates (<, <=, >=, >) or a single equality predicate that compares time attributes of the same type (i.e., processing time or event time) of both input tables.
-
For example, the following predicates are valid window join conditions:
+
A interval join requires at least one equi-join predicate and a join condition that bounds the time on both sides. Such a condition can be defined by two appropriate range predicates (<, <=, >=, >) or a single equality predicate that compares time attributes of the same type (i.e., processing time or event time) of both input tables.
+
For example, the following predicates are valid interval join conditions:
Similar to the INSERT INTO clause in a SQL query. Performs a insertion into a registered output table.
+
Similar to the `INSERT INTO` clause in a SQL query, the method performs an insertion into a registered output table. The `executeInsert()` method will immediately submit a Flink job which execute the insert operation.
Output tables must be registered in the TableEnvironment (see Register a TableSink). Moreover, the schema of the registered table must match the schema of the query.
Similar to the INSERT INTO clause in a SQL query. Performs a insertion into a registered output table.
+
Similar to the `INSERT INTO` clause in a SQL query, the method performs an insertion into a registered output table. The `executeInsert()` method will immediately submit a Flink job which execute the insert operation.
Output tables must be registered in the TableEnvironment (see Connector tables). Moreover, the schema of the registered table must match the schema of the query.
@@ -2185,10 +2287,10 @@ Tumbling windows are defined by using the `Tumble` class as follows:
{% highlight java %}
// Tumbling Event-time Window
-.window(Tumble.over(interval(Duration.ofMinutes(10))).on($("rowtime")).as("w"));
+.window(Tumble.over(lit(10).minutes()).on($("rowtime")).as("w"));
// Tumbling Processing-time Window (assuming a processing-time attribute "proctime")
-.window(Tumble.over(interval(Duration.ofMinutes(10))).on($("proctime")).as("w"));
+.window(Tumble.over(lit(10).minutes()).on($("proctime")).as("w"));
// Tumbling Row-count Window (assuming a processing-time attribute "proctime")
.window(Tumble.over(rowInterval(10)).on($("proctime")).as("w"));
@@ -2260,14 +2362,14 @@ Sliding windows are defined by using the `Slide` class as follows:
{% highlight java %}
// Sliding Event-time Window
-.window(Slide.over(interval(Duration.ofMinutes(10)))
- .every(interval(Duration.ofMinutes(5)))
+.window(Slide.over(lit(10).minutes())
+ .every(lit(5).minutes())
.on($("rowtime"))
.as("w"));
// Sliding Processing-time window (assuming a processing-time attribute "proctime")
-.window(Slide.over(interval(Duration.ofMinutes(10)))
- .every(interval(Duration.ofMinutes(5)))
+.window(Slide.over(lit(10).minutes())
+ .every(lit(5).minutes())
.on($("proctime"))
.as("w"));
@@ -2337,10 +2439,10 @@ A session window is defined by using the `Session` class as follows:
@@ -2526,10 +2628,10 @@ The `OverWindow` defines a range of rows over which aggregates are computed. `Ov
{% highlight java %}
// Bounded Event-time over window (assuming an event-time attribute "rowtime")
-.window(Over.partitionBy($("a")).orderBy($("rowtime")).preceding(interval(Duration.ofMinutes(1))).as("w"))
+.window(Over.partitionBy($("a")).orderBy($("rowtime")).preceding(lit(1).minutes()).as("w"))
// Bounded Processing-time over window (assuming a processing-time attribute "proctime")
-.window(Over.partitionBy($("a")).orderBy($("proctime")).preceding(interval(Duration.ofMinutes(1))).as("w"))
+.window(Over.partitionBy($("a")).orderBy($("proctime")).preceding(lit(1).minutes()).as("w"))
// Bounded Event-time Row-count over window (assuming an event-time attribute "rowtime")
.window(Over.partitionBy($("a")).orderBy($("rowtime")).preceding(rowInterval(10)).as("w"))
@@ -2717,7 +2819,7 @@ AggregateFunction myAggFunc = new MyMinMax();
tableEnv.registerFunction("myAggFunc", myAggFunc);
Table table = input
- .window(Tumble.over(interval(Duration.ofMinutes(5)))
+ .window(Tumble.over(lit(5).minutes())
.on($("rowtime"))
.as("w")) // define window
.groupBy($("key"), $("w")) // group by key and window
@@ -2809,7 +2911,7 @@ Table result = orders
tableEnv.registerFunction("top2", new Top2());
Table orders = tableEnv.from("Orders");
Table result = orders
- .window(Tumble.over(interval(Duration.ofMinutes(5)))
+ .window(Tumble.over(lit(5).minutes())
.on($("rowtime"))
.as("w")) // define window
.groupBy($("a"), $("w")) // group by key and window
@@ -3157,6 +3259,6 @@ timeIndicator = fieldReference , "." , ( "proctime" | "rowtime" ) ;
**Temporal intervals:** Temporal intervals can be represented as number of months (`Types.INTERVAL_MONTHS`) or number of milliseconds (`Types.INTERVAL_MILLIS`). Intervals of same type can be added or subtracted (e.g. `1.hour + 10.minutes`). Intervals of milliseconds can be added to time points (e.g. `"2016-08-10".toDate + 5.days`).
-**Scala expressions:** Scala expressions use implicit conversions. Therefore, make sure to add the wildcard import `org.apache.flink.table.api.scala._` to your programs. In case a literal is not treated as an expression, use `.toExpr` such as `3.toExpr` to force a literal to be converted.
+**Scala expressions:** Scala expressions use implicit conversions. Therefore, make sure to add the wildcard import `org.apache.flink.table.api._` to your programs. In case a literal is not treated as an expression, use `.toExpr` such as `3.toExpr` to force a literal to be converted.
{% top %}
diff --git a/docs/dev/table/types.md b/docs/dev/table/types.md
index 4dc42af4270b90..45b6a9456416e3 100644
--- a/docs/dev/table/types.md
+++ b/docs/dev/table/types.md
@@ -36,7 +36,7 @@ Starting with Flink 1.9, the Table & SQL API will receive a new type system that
solution for API stability and standard compliance.
Reworking the type system is a major effort that touches almost all user-facing interfaces. Therefore, its
-introduction spans multiple releases, and the community aims to finish this effort by Flink 1.10.
+introduction spans multiple releases, and the community aims to finish this effort by Flink 1.12.
Due to the simultaneous addition of a new planner for table programs (see [FLINK-11439](https://issues.apache.org/jira/browse/FLINK-11439)),
not every combination of planner and data type is supported. Furthermore, planners might not support every
@@ -211,14 +211,15 @@ The following data types are supported:
| `DOUBLE` | |
| `DATE` | |
| `TIME` | Supports only a precision of `0`. |
-| `TIMESTAMP` | Supports only a precision of `3`. |
-| `TIMESTAMP WITH LOCAL TIME ZONE` | Supports only a precision of `3`. |
+| `TIMESTAMP` | |
+| `TIMESTAMP WITH LOCAL TIME ZONE` | |
| `INTERVAL` | Supports only interval of `MONTH` and `SECOND(3)`. |
| `ARRAY` | |
| `MULTISET` | |
| `MAP` | |
| `ROW` | |
| `RAW` | |
+| stuctured types | Only exposed in user-defined functions yet. |
Limitations
-----------
@@ -227,10 +228,7 @@ Limitations
have not been updated to the new type system yet. Use the string representations declared in
the [old planner section](#old-planner).
-**Connector Descriptors and SQL Client**: Descriptor string representations have not been updated to the new
-type system yet. Use the string representation declared in the [Connect to External Systems section](./connect.html#type-strings)
-
-**User-defined Functions**: User-defined functions cannot declare a data type yet.
+**User-defined Functions**: User-defined aggregate functions cannot declare a data type yet. Scalar and table functions fully support data types.
List of Data Types
------------------
@@ -267,10 +265,11 @@ and `2,147,483,647` (both inclusive). If no length is specified, `n` is equal to
**Bridging to JVM Types**
-| Java Type | Input | Output | Remarks |
-|:-------------------|:-----:|:------:|:------------------------|
-|`java.lang.String` | X | X | *Default* |
-|`byte[]` | X | X | Assumes UTF-8 encoding. |
+| Java Type | Input | Output | Remarks |
+|:----------------------------------------|:-----:|:------:|:-------------------------|
+|`java.lang.String` | X | X | *Default* |
+|`byte[]` | X | X | Assumes UTF-8 encoding. |
+|`org.apache.flink.table.data.StringData` | X | X | Internal data structure. |
#### `VARCHAR` / `STRING`
@@ -306,10 +305,11 @@ between `1` and `2,147,483,647` (both inclusive). If no length is specified, `n`
**Bridging to JVM Types**
-| Java Type | Input | Output | Remarks |
-|:-------------------|:-----:|:------:|:------------------------|
-|`java.lang.String` | X | X | *Default* |
-|`byte[]` | X | X | Assumes UTF-8 encoding. |
+| Java Type | Input | Output | Remarks |
+|:----------------------------------------|:-----:|:------:|:-------------------------|
+|`java.lang.String` | X | X | *Default* |
+|`byte[]` | X | X | Assumes UTF-8 encoding. |
+|`org.apache.flink.table.data.StringData` | X | X | Internal data structure. |
### Binary Strings
@@ -428,9 +428,10 @@ The default value for `s` is `0`.
**Bridging to JVM Types**
-| Java Type | Input | Output | Remarks |
-|:----------------------|:-----:|:------:|:------------------------|
-|`java.math.BigDecimal` | X | X | *Default* |
+| Java Type | Input | Output | Remarks |
+|:-----------------------------------------|:-----:|:------:|:-------------------------|
+|`java.math.BigDecimal` | X | X | *Default* |
+|`org.apache.flink.table.data.DecimalData` | X | X | Internal data structure. |
#### `TINYINT`
@@ -459,7 +460,7 @@ DataTypes.TINYINT()
| Java Type | Input | Output | Remarks |
|:-------------------|:-----:|:------:|:---------------------------------------------|
|`java.lang.Byte` | X | X | *Default* |
-|`byte` | X | (X) | Output only if type is not nullable. |
+|`byte` | X | (X) | Output only if type is not nullable. |
#### `SMALLINT`
@@ -488,7 +489,7 @@ DataTypes.SMALLINT()
| Java Type | Input | Output | Remarks |
|:-------------------|:-----:|:------:|:---------------------------------------------|
|`java.lang.Short` | X | X | *Default* |
-|`short` | X | (X) | Output only if type is not nullable. |
+|`short` | X | (X) | Output only if type is not nullable. |
#### `INT`
@@ -521,7 +522,7 @@ DataTypes.INT()
| Java Type | Input | Output | Remarks |
|:-------------------|:-----:|:------:|:---------------------------------------------|
|`java.lang.Integer` | X | X | *Default* |
-|`int` | X | (X) | Output only if type is not nullable. |
+|`int` | X | (X) | Output only if type is not nullable. |
#### `BIGINT`
@@ -551,7 +552,7 @@ DataTypes.BIGINT()
| Java Type | Input | Output | Remarks |
|:-------------------|:-----:|:------:|:---------------------------------------------|
|`java.lang.Long` | X | X | *Default* |
-|`long` | X | (X) | Output only if type is not nullable. |
+|`long` | X | (X) | Output only if type is not nullable. |
### Approximate Numerics
@@ -584,7 +585,7 @@ DataTypes.FLOAT()
| Java Type | Input | Output | Remarks |
|:-------------------|:-----:|:------:|:---------------------------------------------|
|`java.lang.Float` | X | X | *Default* |
-|`float` | X | (X) | Output only if type is not nullable. |
+|`float` | X | (X) | Output only if type is not nullable. |
#### `DOUBLE`
@@ -617,7 +618,7 @@ DataTypes.DOUBLE()
| Java Type | Input | Output | Remarks |
|:-------------------|:-----:|:------:|:---------------------------------------------|
|`java.lang.Double` | X | X | *Default* |
-|`double` | X | (X) | Output only if type is not nullable. |
+|`double` | X | (X) | Output only if type is not nullable. |
### Date and Time
@@ -741,10 +742,11 @@ is specified, `p` is equal to `6`.
**Bridging to JVM Types**
-| Java Type | Input | Output | Remarks |
-|:-------------------------|:-----:|:------:|:----------------------------------------------------|
-|`java.time.LocalDateTime` | X | X | *Default* |
-|`java.sql.Timestamp` | X | X | |
+| Java Type | Input | Output | Remarks |
+|:-------------------------------------------|:-----:|:------:|:-------------------------|
+|`java.time.LocalDateTime` | X | X | *Default* |
+|`java.sql.Timestamp` | X | X | |
+|`org.apache.flink.table.data.TimestampData` | X | X | Internal data structure. |
#### `TIMESTAMP WITH TIME ZONE`
@@ -837,6 +839,7 @@ of digits of fractional seconds (*precision*). `p` must have a value between `0`
|`int` | X | (X) | Describes the number of seconds since epoch. Output only if type is not nullable. |
|`java.lang.Long` | X | X | Describes the number of milliseconds since epoch. |
|`long` | X | (X) | Describes the number of milliseconds since epoch. Output only if type is not nullable. |
+|`org.apache.flink.table.data.TimestampData` | X | X | Internal data structure. |
#### `INTERVAL YEAR TO MONTH`
@@ -1001,9 +1004,10 @@ equivalent to `ARRAY`.
**Bridging to JVM Types**
-| Java Type | Input | Output | Remarks |
-|:----------|:-----:|:------:|:----------------------------------|
-|*t*`[]` | (X) | (X) | Depends on the subtype. *Default* |
+| Java Type | Input | Output | Remarks |
+|:---------------------------------------|:-----:|:------:|:----------------------------------|
+|*t*`[]` | (X) | (X) | Depends on the subtype. *Default* |
+|`org.apache.flink.table.data.ArrayData` | X | X | Internal data structure. |
#### `MAP`
@@ -1037,10 +1041,11 @@ and `vt` is the data type of the value elements.
**Bridging to JVM Types**
-| Java Type | Input | Output | Remarks |
-|:--------------------------------------|:-----:|:------:|:----------|
-| `java.util.Map` | X | X | *Default* |
-| *subclass* of `java.util.Map` | X | | |
+| Java Type | Input | Output | Remarks |
+|:--------------------------------------|:-----:|:------:|:-------------------------|
+| `java.util.Map` | X | X | *Default* |
+| *subclass* of `java.util.Map` | X | | |
+|`org.apache.flink.table.data.MapData` | X | X | Internal data structure. |
#### `MULTISET`
@@ -1076,10 +1081,11 @@ equivalent to `MULTISET`.
**Bridging to JVM Types**
-| Java Type | Input | Output | Remarks |
-|:-------------------------------------|:-----:|:------:|:---------------------------------------------------------|
-|`java.util.Map` | X | X | Assigns each value to an integer multiplicity. *Default* |
-| *subclass* of `java.util.Map` | X | | Assigns each value to an integer multiplicity. |
+| Java Type | Input | Output | Remarks |
+|:--------------------------------------|:-----:|:------:|:---------------------------------------------------------|
+|`java.util.Map` | X | X | Assigns each value to an integer multiplicity. *Default* |
+| *subclass* of `java.util.Map>` | X | | |
+|`org.apache.flink.table.data.MapData` | X | X | Internal data structure. |
#### `ROW`
@@ -1125,9 +1131,110 @@ equivalent to `ROW`.
**Bridging to JVM Types**
-| Java Type | Input | Output | Remarks |
-|:----------------------------|:-----:|:------:|:------------------------|
-|`org.apache.flink.types.Row` | X | X | *Default* |
+| Java Type | Input | Output | Remarks |
+|:-------------------------------------|:-----:|:------:|:-------------------------|
+|`org.apache.flink.types.Row` | X | X | *Default* |
+|`org.apache.flink.table.data.RowData` | X | X | Internal data structure. |
+
+### User-Defined Data Types
+
+Attention User-defined data types are not fully supported yet. They are
+currently (as of Flink 1.11) only exposed as unregistered structured types in parameters and return types of functions.
+
+A structured type is similar to an object in an object-oriented programming language. It contains
+zero, one or more attributes. Each attribute consists of a name and a type.
+
+There are two kinds of structured types:
+
+- Types that are stored in a catalog and are identified by a _catalog identifer_ (like `cat.db.MyType`). Those
+are equal to the SQL standard definition of structured types.
+
+- Anonymously defined, unregistered types (usually reflectively extracted) that are identified by
+an _implementation class_ (like `com.myorg.model.MyType`). Those are useful when programmatically
+defining a table program. They enable reusing existing JVM classes without manually defining the
+schema of a data type again.
+
+#### Registered Structured Types
+
+Currently, registered structured types are not supported. Thus, they cannot be stored in a catalog
+or referenced in a `CREATE TABLE` DDL.
+
+#### Unregistered Structured Types
+
+Unregistered structured types can be created from regular POJOs (Plain Old Java Objects) using automatic reflective extraction.
+
+The implementation class of a structured type must meet the following requirements:
+- The class must be globally accessible which means it must be declared `public`, `static`, and not `abstract`.
+- The class must offer a default constructor with zero arguments or a full constructor that assigns all
+fields.
+- All fields of the class must be readable by either `public` declaration or a getter that follows common
+coding style such as `getField()`, `isField()`, `field()`.
+- All fields of the class must be writable by either `public` declaration, fully assigning constructor,
+or a setter that follows common coding style such as `setField(...)`, `field(...)`.
+- All fields must be mapped to a data type either implicitly via reflective extraction or explicitly
+using the `@DataTypeHint` [annotations](#data-type-annotations).
+- Fields that are declared `static` or `transient` are ignored.
+
+The reflective extraction supports arbitrary nesting of fields as long as a field type does not
+(transitively) refer to itself.
+
+The declared field class (e.g. `public int age;`) must be contained in the list of supported JVM
+bridging classes defined for every data type in this document (e.g. `java.lang.Integer` or `int` for `INT`).
+
+For some classes an annotation is required in order to map the class to a data type (e.g. `@DataTypeHint("DECIMAL(10, 2)")`
+to assign a fixed precision and scale for `java.math.BigDecimal`).
+
+**Declaration**
+
+
+
+
+{% highlight java %}
+class User {
+
+ // extract fields automatically
+ public int age;
+ public String name;
+
+ // enrich the extraction with precision information
+ public @DataTypeHint("DECIMAL(10, 2)") BigDecimal totalBalance;
+
+ // enrich the extraction with forcing using RAW types
+ public @DataTypeHint("RAW") Class> modelClass;
+}
+
+DataTypes.of(User.class);
+{% endhighlight %}
+
+
+
+{% highlight scala %}
+case class User(
+
+ // extract fields automatically
+ age: Int,
+ name: String,
+
+ // enrich the extraction with precision information
+ @DataTypeHint("DECIMAL(10, 2)") totalBalance: java.math.BigDecimal,
+
+ // enrich the extraction with forcing using a RAW type
+ @DataTypeHint("RAW") modelClass: Class[_]
+)
+
+DataTypes.of(classOf[User])
+{% endhighlight %}
+
+
+
+
+**Bridging to JVM Types**
+
+| Java Type | Input | Output | Remarks |
+|:-------------------------------------|:-----:|:------:|:----------------------------------------|
+|*class* | X | X | Originating class or subclasses (for input) or superclasses (for output). *Default* |
+|`org.apache.flink.types.Row` | X | X | Represent the structured type as a row. |
+|`org.apache.flink.table.data.RowData` | X | X | Internal data structure. |
### Other Data Types
@@ -1160,6 +1267,48 @@ DataTypes.BOOLEAN()
|`java.lang.Boolean` | X | X | *Default* |
|`boolean` | X | (X) | Output only if type is not nullable. |
+#### `RAW`
+
+Data type of an arbitrary serialized type. This type is a black box within the table ecosystem
+and is only deserialized at the edges.
+
+The raw type is an extension to the SQL standard.
+
+**Declaration**
+
+
+
+
+{% highlight text %}
+RAW('class', 'snapshot')
+{% endhighlight %}
+
+
+The type can be declared using `RAW('class', 'snapshot')` where `class` is the originating class and
+`snapshot` is the serialized `TypeSerializerSnapshot` in Base64 encoding. Usually, the type string is not
+declared directly but is generated while persisting the type.
+
+In the API, the `RAW` type can be declared either by directly supplying a `Class` + `TypeSerializer` or
+by passing `Class` and letting the framework extract `Class` + `TypeSerializer` from there.
+
+**Bridging to JVM Types**
+
+| Java Type | Input | Output | Remarks |
+|:------------------|:-----:|:------:|:-------------------------------------------|
+|*class* | X | X | Originating class or subclasses (for input) or superclasses (for output). *Default* |
+|`byte[]` | | X | |
+|`org.apache.flink.table.data.RawValueData` | X | X | Internal data structure. |
+
#### `NULL`
Data type for representing untyped `NULL` values.
@@ -1197,45 +1346,122 @@ DataTypes.NULL()
|`java.lang.Object` | X | X | *Default* |
|*any class* | | (X) | Any non-primitive type. |
-#### `RAW`
+Data Type Extraction
+--------------------
+
+At many locations in the API, Flink tries to automatically extract data type from class information using
+reflection to avoid repetitive manual schema work. However, extracting a data type reflectively is not always
+successful because logical information might be missing. Therefore, it might be necessary to add additional
+information close to a class or field declaration for supporting the extraction logic.
+
+The following table lists classes that can be implicitly mapped to a data type without requiring further information.
+
+If you intend to implement classes in Scala, *it is recommended to use boxed types* (e.g. `java.lang.Integer`)
+instead of Scala's primitives. Scala's primitives (e.g. `Int` or `Double`) are compiled to JVM primitives (e.g.
+`int`/`double`) and result in `NOT NULL` semantics as shown in the table below. Furthermore, Scala primitives that
+are used in generics (e.g. `java.lang.Map[Int, Double]`) are erased during compilation and lead to class
+information similar to `java.lang.Map[java.lang.Object, java.lang.Object]`.
+
+| Class | Data Type |
+|:----------------------------|:------------------------------------|
+| `java.lang.String` | `STRING` |
+| `java.lang.Boolean` | `BOOLEAN` |
+| `boolean` | `BOOLEAN NOT NULL` |
+| `java.lang.Byte` | `TINYINT` |
+| `byte` | `TINYINT NOT NULL` |
+| `java.lang.Short` | `SMALLINT` |
+| `short` | `SMALLINT NOT NULL` |
+| `java.lang.Integer` | `INT` |
+| `int` | `INT NOT NULL` |
+| `java.lang.Long` | `BIGINT` |
+| `long` | `BIGINT NOT NULL` |
+| `java.lang.Float` | `FLOAT` |
+| `float` | `FLOAT NOT NULL` |
+| `java.lang.Double` | `DOUBLE` |
+| `double` | `DOUBLE NOT NULL` |
+| `java.sql.Date` | `DATE` |
+| `java.time.LocalDate` | `DATE` |
+| `java.sql.Time` | `TIME(0)` |
+| `java.time.LocalTime` | `TIME(9)` |
+| `java.sql.Timestamp` | `TIMESTAMP(9)` |
+| `java.time.LocalDateTime` | `TIMESTAMP(9)` |
+| `java.time.OffsetDateTime` | `TIMESTAMP(9) WITH TIME ZONE` |
+| `java.time.Instant` | `TIMESTAMP(9) WITH LOCAL TIME ZONE` |
+| `java.time.Duration` | `INVERVAL SECOND(9)` |
+| `java.time.Period` | `INTERVAL YEAR(4) TO MONTH` |
+| `byte[]` | `BYTES` |
+| `T[]` | `ARRAY` |
+| `java.lang.Map` | `MAP` |
+| structured type `T` | anonymous structured type `T` |
+
+Other JVM bridging classes mentioned in this document require a `@DataTypeHint` annotation.
+
+_Data type hints_ can parameterize or replace the default extraction logic of individual function parameters
+and return types, structured classes, or fields of structured classes. An implementer can choose to what
+extent the default extraction logic should be modified by declaring a `@DataTypeHint` annotation.
+
+The `@DataTypeHint` annotation provides a set of optional hint parameters. Some of those parameters are shown in the
+following example. More information can be found in the documentation of the annotation class.
-Data type of an arbitrary serialized type. This type is a black box within the table ecosystem
-and is only deserialized at the edges.
+
-The raw type is an extension to the SQL standard.
+
+ // defines an INT data type with a default conversion class `java.lang.Integer`
+ public @DataTypeHint("INT") Object o;
-
-{% highlight text %}
-RAW('class', 'snapshot')
+ // defines a TIMESTAMP data type of millisecond precision with an explicit conversion class
+ public @DataTypeHint(value = "TIMESTAMP(3)", bridgedTo = java.sql.Timestamp.class) Object o;
+
+ // enrich the extraction with forcing using a RAW type
+ public @DataTypeHint("RAW") Class> modelClass;
+
+ // defines that all occurrences of java.math.BigDecimal (also in nested fields) will be
+ // extracted as DECIMAL(12, 2)
+ public @DataTypeHint(defaultDecimalPrecision = 12, defaultDecimalScale = 2) AccountStatement stmt;
+
+ // defines that whenever a type cannot be mapped to a data type, instead of throwing
+ // an exception, always treat it as a RAW type
+ public @DataTypeHint(allowRawGlobally = HintFlag.TRUE) ComplexModel model;
+}
{% endhighlight %}
+ // defines an INT data type with a default conversion class `java.lang.Integer`
+ @DataTypeHint("INT")
+ var o: AnyRef
-The type can be declared using `RAW('class', 'snapshot')` where `class` is the originating class and
-`snapshot` is the serialized `TypeSerializerSnapshot` in Base64 encoding. Usually, the type string is not
-declared directly but is generated while persisting the type.
+ // defines a TIMESTAMP data type of millisecond precision with an explicit conversion class
+ @DataTypeHint(value = "TIMESTAMP(3)", bridgedTo = java.sql.Timestamp.class)
+ var o: AnyRef
-In the API, the `RAW` type can be declared either by directly supplying a `Class` + `TypeSerializer` or
-by passing `TypeInformation` and let the framework extract `Class` + `TypeSerializer` from there.
+ // enrich the extraction with forcing using a RAW type
+ @DataTypeHint("RAW")
+ var modelClass: Class[_]
-**Bridging to JVM Types**
+ // defines that all occurrences of java.math.BigDecimal (also in nested fields) will be
+ // extracted as DECIMAL(12, 2)
+ @DataTypeHint(defaultDecimalPrecision = 12, defaultDecimalScale = 2)
+ var stmt: AccountStatement
-| Java Type | Input | Output | Remarks |
-|:------------------|:-----:|:------:|:-------------------------------------------|
-|*class* | X | X | Originating class or subclasses (for input) or superclasses (for output). *Default* |
-|`byte[]` | | X | |
+ // defines that whenever a type cannot be mapped to a data type, instead of throwing
+ // an exception, always treat it as a RAW type
+ @DataTypeHint(allowRawGlobally = HintFlag.TRUE)
+ var model: ComplexModel
+}
+{% endhighlight %}
+
+
+
{% top %}
diff --git a/docs/dev/table/types.zh.md b/docs/dev/table/types.zh.md
index 252ff021bf1f4c..819abc83af92c2 100644
--- a/docs/dev/table/types.zh.md
+++ b/docs/dev/table/types.zh.md
@@ -28,7 +28,7 @@ under the License.
从 Flink 1.9 开始,Table & SQL API 开始启用一种新的类型系统作为长期解决方案,用来保持 API 稳定性和 SQL 标准的兼容性。
-重新设计类型系统是一项涉及几乎所有的面向用户接口的重大工作。因此,它的引入跨越多个版本,社区的目标是在 Flink 1.10 完成这项工作。
+重新设计类型系统是一项涉及几乎所有的面向用户接口的重大工作。因此,它的引入跨越多个版本,社区的目标是在 Flink 1.12 完成这项工作。
同时由于为 Table 编程添加了新的 Planner 详见([FLINK-11439](https://issues.apache.org/jira/browse/FLINK-11439)), 并不是每种 Planner 都支持所有的数据类型。此外,Planner 对于数据类型的精度和参数化支持也可能是不完整的。
@@ -112,7 +112,7 @@ DataType t = DataTypes.ARRAY(DataTypes.INT().notNull()).bridgedTo(int[].class);
// 而是使用 java.sql.Timestamp
val t: DataType = DataTypes.TIMESTAMP(3).bridgedTo(classOf[java.sql.Timestamp]);
-// 告诉运行时不要产生或者消费装箱的整数数组
+// 告诉运行时不要产生或者消费装箱的整数数组
// 而是使用基本数据类型的整数数组
val t: DataType = DataTypes.ARRAY(DataTypes.INT().notNull()).bridgedTo(classOf[Array[Int]]);
{% endhighlight %}
@@ -184,23 +184,22 @@ Flink 1.9 之前引入的旧的 Planner 主要支持类型信息(Type Informat
| `DOUBLE` | |
| `DATE` | |
| `TIME` | 支持的精度仅为 `0`。 |
-| `TIMESTAMP` | 支持的精度仅为 `3`。 |
-| `TIMESTAMP WITH LOCAL TIME ZONE` | 支持的精度仅为 `3`。 |
+| `TIMESTAMP` | |
+| `TIMESTAMP WITH LOCAL TIME ZONE` | |
| `INTERVAL` | 仅支持 `MONTH` 和 `SECOND(3)` 区间。 |
| `ARRAY` | |
| `MULTISET` | |
| `MAP` | |
| `ROW` | |
| `RAW` | |
+| stuctured types | 暂只能在用户自定义函数里使用。 |
局限性
-----------
**Java 表达式字符串**:Table API 中的 Java 表达式字符串,例如 `table.select("field.cast(STRING)")`,尚未被更新到新的类型系统中,使用[旧的 Planner 章节](#旧的-planner)中声明的字符串来表示。
-**连接器描述符和 SQL 客户端**:描述符字符串的表示形式尚未更新到新的类型系统。使用在[连接到外部系统章节](./connect.html#type-strings)中声明的字符串表示。
-
-**用户自定义函数**:用户自定义函数尚不能声明数据类型。
+**用户自定义函数**:用户自定义聚合函数尚不能声明数据类型,标量函数和表函数充分支持数据类型。
数据类型列表
------------------
@@ -236,10 +235,11 @@ DataTypes.CHAR(n)
**JVM 类型**
-| Java 类型 | 输入 | 输出 | 备注 |
-|:-------------------|:-----:|:------:|:------------------------|
-|`java.lang.String` | X | X | *缺省* |
-|`byte[]` | X | X | 假设使用 UTF-8 编码。 |
+| Java 类型 | 输入 | 输出 | 备注 |
+|:----------------------------------------|:-----:|:------:|:------------------------|
+|`java.lang.String` | X | X | *缺省* |
+|`byte[]` | X | X | 假设使用 UTF-8 编码。 |
+|`org.apache.flink.table.data.StringData` | X | X | 内部数据结构。 |
#### `VARCHAR` / `STRING`
@@ -274,10 +274,11 @@ DataTypes.STRING()
**JVM 类型**
-| Java 类型 | 输入 | 输出 | 备注 |
-|:-------------------|:-----:|:------:|:------------------------|
-|`java.lang.String` | X | X | *缺省* |
-|`byte[]` | X | X | 假设使用 UTF-8 编码。 |
+| Java 类型 | 输入 | 输出 | 备注 |
+|:----------------------------------------|:-----:|:------:|:------------------------|
+|`java.lang.String` | X | X | *缺省* |
+|`byte[]` | X | X | 假设使用 UTF-8 编码。 |
+|`org.apache.flink.table.data.StringData` | X | X | 内部数据结构。 |
### 二进制字符串
@@ -389,9 +390,10 @@ DataTypes.DECIMAL(p, s)
**JVM 类型**
-| Java 类型 | 输入 | 输出 | 备注 |
-|:----------------------|:-----:|:------:|:------------------------|
-|`java.math.BigDecimal` | X | X | *缺省* |
+| Java 类型 | 输入 | 输出 | 备注 |
+|:-----------------------------------------|:-----:|:------:|:------------------------|
+|`java.math.BigDecimal` | X | X | *缺省* |
+|`org.apache.flink.table.data.DecimalData` | X | X | 内部数据结构。 |
#### `TINYINT`
@@ -690,12 +692,13 @@ DataTypes.TIMESTAMP(p)
| Java 类型 | 输入 | 输出 | 备注 |
|:-------------------------|:-----:|:------:|:----------------------------------------------------|
-|`java.time.LocalDateTime` | X | X | *缺省* |
-|`java.sql.Timestamp` | X | X | |
+|`java.time.LocalDateTime` | X | X | *缺省* |
+|`java.sql.Timestamp` | X | X | |
+|`org.apache.flink.table.data.TimestampData` | X | X | 内部数据结构。 |
#### `TIMESTAMP WITH TIME ZONE`
-*带有*时区的时间戳数据类型,由 `year-month-day hour:minute:second[.fractional] zone` 组成,精度达到纳秒,范围从 `0000-01-01 00:00:00.000000000 +14:59` 到
+*带有*时区的时间戳数据类型,由 `year-month-day hour:minute:second[.fractional] zone` 组成,精度达到纳秒,范围从 `0000-01-01 00:00:00.000000000 +14:59` 到
`9999-12-31 23:59:59.999999999 -14:59`。
与 SQL 标准相比,不支持闰秒(`23:59:60` 和 `23:59:61`),语义上更接近于 `java.time.OffsetDateTime`。
@@ -732,7 +735,7 @@ DataTypes.TIMESTAMP_WITH_TIME_ZONE(p)
#### `TIMESTAMP WITH LOCAL TIME ZONE`
-*带有本地*时区的时间戳数据类型,由 `year-month-day hour:minute:second[.fractional] zone` 组成,精度达到纳秒,范围从 `0000-01-01 00:00:00.000000000 +14:59` 到
+*带有本地*时区的时间戳数据类型,由 `year-month-day hour:minute:second[.fractional] zone` 组成,精度达到纳秒,范围从 `0000-01-01 00:00:00.000000000 +14:59` 到
`9999-12-31 23:59:59.999999999 -14:59`。
不支持闰秒(`23:59:60` 和 `23:59:61`),语义上更接近于 `java.time.OffsetDateTime`。
@@ -764,13 +767,14 @@ DataTypes.TIMESTAMP_WITH_LOCAL_TIME_ZONE(p)
**JVM 类型**
-| Java 类型 |输入 |输出 |备注 |
-|:-------------------|:-----:|:------:|:--------------------------------------------------|
-|`java.time.Instant` | X | X | *缺省* |
-|`java.lang.Integer` | X | X | 描述从 Epoch 算起的秒数。 |
-|`int` | X | (X) | 描述从 Epoch 算起的秒数。 仅当类型不可为空时才输出。 |
-|`java.lang.Long` | X | X | 描述从 Epoch 算起的毫秒数。 |
-|`long` | X | (X) | 描述从 Epoch 算起的毫秒数。 仅当类型不可为空时才输出。 |
+| Java 类型 |输入 |输出 |备注 |
+|:-------------------------------------------|:-----:|:------:|:--------------------------------------------------|
+|`java.time.Instant` | X | X | *缺省* |
+|`java.lang.Integer` | X | X | 描述从 Epoch 算起的秒数。 |
+|`int` | X | (X) | 描述从 Epoch 算起的秒数。 仅当类型不可为空时才输出。 |
+|`java.lang.Long` | X | X | 描述从 Epoch 算起的毫秒数。 |
+|`long` | X | (X) | 描述从 Epoch 算起的毫秒数。 仅当类型不可为空时才输出 |
+|`org.apache.flink.table.data.TimestampData` | X | X | 内部数据结构。 |
#### `INTERVAL YEAR TO MONTH`
@@ -922,9 +926,10 @@ DataTypes.ARRAY(t)
**JVM 类型**
-| Java 类型 | 输入 | 输出 | 备注 |
-|:----------|:-----:|:------:|:----------------------------------|
-|*t*`[]` | (X) | (X) | 依赖于子类型。 *缺省* |
+| Java 类型 | 输入 | 输出 | 备注 |
+|:---------------------------------------|:-----:|:------:|:----------------------------------|
+|*t*`[]` | (X) | (X) | 依赖于子类型。 *缺省* |
+|`org.apache.flink.table.data.ArrayData` | X | X | 内部数据结构。 |
#### `MAP`
@@ -956,10 +961,11 @@ DataTypes.MAP(kt, vt)
**JVM 类型**
-| Java 类型 | 输入 | 输出 | 备注 |
-|:--------------------------------------|:-----:|:------:|:----------|
-| `java.util.Map` | X | X | *缺省* |
-| `java.util.Map` 的*子类型* | X | | |
+| Java 类型 | 输入 | 输出 | 备注 |
+|:--------------------------------------|:-----:|:------:|:---------------|
+| `java.util.Map` | X | X | *缺省* |
+| `java.util.Map` 的*子类型* | X | | |
+|`org.apache.flink.table.data.MapData` | X | X | 内部数据结构。 |
#### `MULTISET`
@@ -992,10 +998,11 @@ DataTypes.MULTISET(t)
**JVM 类型**
-| Java 类型 | 输入 | 输出 | 备注 |
-|:-------------------------------------|:-----:|:------:|:---------------------------------------------------------|
-|`java.util.Map` | X | X | 将每个值可多重地分配给一个整数 *缺省* |
-|`java.util.Map` 的*子类型*| X | | 将每个值可多重地分配给一个整数 |
+| Java 类型 | 输入 | 输出 | 备注 |
+|:-------------------------------------------------|:-----:|:------:|:------------------------------------------------------|
+|`java.util.Map` | X | X | 将每个值可多重地分配给一个整数 *缺省* |
+| `java.util.Map` 的*子类型* | X | | |
+|`org.apache.flink.table.data.MapData` | X | X | 内部数据结构。 |
#### `ROW`
@@ -1036,9 +1043,96 @@ DataTypes.ROW(DataTypes.FIELD(n0, t0, d0), DataTypes.FIELD(n1, t1, d1), ...)
**JVM 类型**
-| Java 类型 | 输入 | 输出 | 备注 |
-|:----------------------------|:-----:|:------:|:------------------------|
-|`org.apache.flink.types.Row` | X | X | *缺省* |
+| Java 类型 | 输入 | 输出 | 备注 |
+|:-------------------------------------|:-----:|:------:|:------------------------|
+|`org.apache.flink.types.Row` | X | X | *缺省* |
+|`org.apache.flink.table.data.RowData` | X | X | 内部数据结构。 |
+
+### 用户自定义数据类型
+
+注意 还未完全支持用户自定义数据类型,当前(从 Flink 1.11 开始)它们仅可作为函数参数和返回值的未注册的结构化类型。
+
+结构化类型类似于面向对象编程语言中的对象,可包含零个、一个或多个属性,每个属性都包含一个名称和一个类型。
+
+有两种结构化类型:
+
+- 存储在 catalog 并由 _catatlog 标识符_ 标识的类型(例如 `cat.db.MyType`),等价于 SQL 标准定义里的结构化类型。
+
+- 由 _实现类_ 标识,通常以反射方式匿名定义的未注册类型(例如 `com.myorg.model.MyType`)。当写代码定义表时,这些功能很有用。它们使你能够重用现有的JVM类,而无需重复手动定义数据类型。
+
+#### 可注册的结构化类型
+
+当前尚不支持,因此无法在 catalog 里保存或在 `CREATE TABLE` DDL 语句里引用它们。
+
+#### 未注册的结构化类型
+
+可以从常规 POJOs(Plain Old Java Objects)自动反射式提取出未注册的结构化类型。
+
+结构化类型的实现类必须满足以下要求:
+- 可被全局访问到,即必须声明为 `public`、`static`,不能用 `abstract`;
+- 提供无参默认构造器,或可设置所有成员变量的构造器;
+- 可访问类的所有成员变量,比如使用 `public` 声明成员变量,或遵循通用代码规范写 getter 比如 `getField()`、`isField()`、`field()`;
+- 可设置类的所有成员变量,比如使用 `public` 声明成员变量,定义可设置所有成员变量的构造器,或遵循通用代码规范写 setter 比如 `setField(...)`、`field(...)`;
+- 所有成员变量都要映射到某个数据类型,比如使用反射式提取进行隐式映射,或用 `@DataTypeHint` [注解](#data-type-annotations) 显式映射;
+- 忽略 `static` 或 `transient` 修饰的成员变量;
+
+只要字段不(递归地)指向自己,反射式提取支持字段的任意嵌套。
+
+成员变量(比如 `public int age;`)的类型必须包含在本文为每种数据类型定义的受支持的 JVM 类型列表里(例如,`java.lang.Integer` 或 `int` 对应 `INT`)。
+
+对于某些类,需要有注解才能将类映射到数据类型(例如, `@DataTypeHint("DECIMAL(10, 2)")` 为 `java.math.BigDecimal` 分配固定的精度和小数位)。
+
+**声明**
+
+
+
+
+{% highlight java %}
+class User {
+
+ // extract fields automatically
+ public int age;
+ public String name;
+
+ // enrich the extraction with precision information
+ public @DataTypeHint("DECIMAL(10, 2)") BigDecimal totalBalance;
+
+ // enrich the extraction with forcing using RAW types
+ public @DataTypeHint("RAW") Class> modelClass;
+}
+
+DataTypes.of(User.class);
+{% endhighlight %}
+
+
+
+{% highlight scala %}
+case class User(
+
+ // extract fields automatically
+ age: Int,
+ name: String,
+
+ // enrich the extraction with precision information
+ @DataTypeHint("DECIMAL(10, 2)") totalBalance: java.math.BigDecimal,
+
+ // enrich the extraction with forcing using a RAW type
+ @DataTypeHint("RAW") modelClass: Class[_]
+)
+
+DataTypes.of(classOf[User])
+{% endhighlight %}
+
+
+
+
+**JVM 类型**
+
+| Java 类型 | 输入 | 输出 | 备注 |
+|:-------------------------------------|:-----:|:------:|:------------------------------------------------------|
+|*类型* | X | X | 原始类或子类(用于输入)或超类(用于输出)*缺省* |
+|`org.apache.flink.types.Row` | X | X | 代表一行数据的结构化类型。 |
+|`org.apache.flink.table.data.RowData` | X | X | 内部数据结构。 |
### 其他数据类型
@@ -1071,6 +1165,44 @@ DataTypes.BOOLEAN()
|`java.lang.Boolean` | X | X | *缺省* |
|`boolean` | X | (X) | 仅当类型不可为空时才输出。 |
+#### `RAW`
+
+任意序列化类型的数据类型。此类型对于 Flink Table 来讲是一个黑盒子,仅在跟外部交互时被反序列化。
+
+Raw 类型是 SQL 标准的扩展。
+
+**声明**
+
+
+
+
+{% highlight text %}
+RAW('class', 'snapshot')
+{% endhighlight %}
+
-{% highlight text %}
-RAW('class', 'snapshot')
+ // defines an INT data type with a default conversion class `java.lang.Integer`
+ public @DataTypeHint("INT") Object o;
+
+ // defines a TIMESTAMP data type of millisecond precision with an explicit conversion class
+ public @DataTypeHint(value = "TIMESTAMP(3)", bridgedTo = java.sql.Timestamp.class) Object o;
+
+ // enrich the extraction with forcing using a RAW type
+ public @DataTypeHint("RAW") Class> modelClass;
+
+ // defines that all occurrences of java.math.BigDecimal (also in nested fields) will be
+ // extracted as DECIMAL(12, 2)
+ public @DataTypeHint(defaultDecimalPrecision = 12, defaultDecimalScale = 2) AccountStatement stmt;
+
+ // defines that whenever a type cannot be mapped to a data type, instead of throwing
+ // an exception, always treat it as a RAW type
+ public @DataTypeHint(allowRawGlobally = HintFlag.TRUE) ComplexModel model;
+}
{% endhighlight %}
+ // defines an INT data type with a default conversion class `java.lang.Integer`
+ @DataTypeHint("INT")
+ var o: AnyRef
-此类型用 `RAW('class', 'snapshot')` 声明,其中 `class` 是原始类,`snapshot` 是 Base64 编码的序列化的 `TypeSerializerSnapshot`。通常,类型字符串不是直接声明的,而是在保留类型时生成的。
+ // defines a TIMESTAMP data type of millisecond precision with an explicit conversion class
+ @DataTypeHint(value = "TIMESTAMP(3)", bridgedTo = java.sql.Timestamp.class)
+ var o: AnyRef
-在 API 中,可以通过直接提供 `Class` + `TypeSerializer` 或通过传递 `TypeInformation` 并让框架从那里提取 `Class` + `TypeSerializer` 来声明 `RAW` 类型。
+ // enrich the extraction with forcing using a RAW type
+ @DataTypeHint("RAW")
+ var modelClass: Class[_]
-**JVM 类型**
+ // defines that all occurrences of java.math.BigDecimal (also in nested fields) will be
+ // extracted as DECIMAL(12, 2)
+ @DataTypeHint(defaultDecimalPrecision = 12, defaultDecimalScale = 2)
+ var stmt: AccountStatement
-| Java 类型 | 输入 | 输出 | 备注 |
-|:------------------|:-----:|:------:|:-------------------------------------------|
-|*类型* | X | X | 原始类或子类(用于输入)或超类(用于输出)。 *缺省* |
-|`byte[]` | | X | |
+ // defines that whenever a type cannot be mapped to a data type, instead of throwing
+ // an exception, always treat it as a RAW type
+ @DataTypeHint(allowRawGlobally = HintFlag.TRUE)
+ var model: ComplexModel
+}
+{% endhighlight %}
+
+
+
{% top %}
diff --git a/docs/dev/types_serialization.zh.md b/docs/dev/types_serialization.zh.md
index 72c70cc477039c..5be877f9e353a3 100644
--- a/docs/dev/types_serialization.zh.md
+++ b/docs/dev/types_serialization.zh.md
@@ -30,6 +30,206 @@ Apache Flink 以其独特的方式来处理数据类型以及序列化,这种
* This will be replaced by the TOC
{:toc}
+## Supported Data Types
+
+Flink places some restrictions on the type of elements that can be in a DataSet or DataStream.
+The reason for this is that the system analyzes the types to determine
+efficient execution strategies.
+
+There are seven different categories of data types:
+
+1. **Java Tuples** and **Scala Case Classes**
+2. **Java POJOs**
+3. **Primitive Types**
+4. **Regular Classes**
+5. **Values**
+6. **Hadoop Writables**
+7. **Special Types**
+
+#### Tuples and Case Classes
+
+
+
+
+Tuples are composite types that contain a fixed number of fields with various types.
+The Java API provides classes from `Tuple1` up to `Tuple25`. Every field of a tuple
+can be an arbitrary Flink type including further tuples, resulting in nested tuples. Fields of a
+tuple can be accessed directly using the field's name as `tuple.f4`, or using the generic getter method
+`tuple.getField(int position)`. The field indices start at 0. Note that this stands in contrast
+to the Scala tuples, but it is more consistent with Java's general indexing.
+
+{% highlight java %}
+DataStream> wordCounts = env.fromElements(
+ new Tuple2("hello", 1),
+ new Tuple2("world", 2));
+
+wordCounts.map(new MapFunction, Integer>() {
+ @Override
+ public Integer map(Tuple2 value) throws Exception {
+ return value.f1;
+ }
+});
+
+wordCounts.keyBy(0); // also valid .keyBy("f0")
+
+
+{% endhighlight %}
+
+
+
+
+Scala case classes (and Scala tuples which are a special case of case classes), are composite types that contain a fixed number of fields with various types. Tuple fields are addressed by their 1-offset names such as `_1` for the first field. Case class fields are accessed by their name.
+
+{% highlight scala %}
+case class WordCount(word: String, count: Int)
+val input = env.fromElements(
+ WordCount("hello", 1),
+ WordCount("world", 2)) // Case Class Data Set
+
+input.keyBy("word")// key by field expression "word"
+
+val input2 = env.fromElements(("hello", 1), ("world", 2)) // Tuple2 Data Set
+
+input2.keyBy(0, 1) // key by field positions 0 and 1
+{% endhighlight %}
+
+
+
+
+#### POJOs
+
+Java and Scala classes are treated by Flink as a special POJO data type if they fulfill the following requirements:
+
+- The class must be public.
+
+- It must have a public constructor without arguments (default constructor).
+
+- All fields are either public or must be accessible through getter and setter functions. For a field called `foo` the getter and setter methods must be named `getFoo()` and `setFoo()`.
+
+- The type of a field must be supported by a registered serializer.
+
+POJOs are generally represented with a `PojoTypeInfo` and serialized with the `PojoSerializer` (using [Kryo](https://github.com/EsotericSoftware/kryo) as configurable fallback).
+The exception is when the POJOs are actually Avro types (Avro Specific Records) or produced as "Avro Reflect Types".
+In that case the POJO's are represented by an `AvroTypeInfo` and serialized with the `AvroSerializer`.
+You can also register your own custom serializer if required; see [Serialization](https://ci.apache.org/projects/flink/flink-docs-stable/dev/types_serialization.html#serialization-of-pojo-types) for further information.
+
+Flink analyzes the structure of POJO types, i.e., it learns about the fields of a POJO. As a result POJO types are easier to use than general types. Moreover, Flink can process POJOs more efficiently than general types.
+
+The following example shows a simple POJO with two public fields.
+
+
+
+{% highlight java %}
+public class WordWithCount {
+
+ public String word;
+ public int count;
+
+ public WordWithCount() {}
+
+ public WordWithCount(String word, int count) {
+ this.word = word;
+ this.count = count;
+ }
+}
+
+DataStream wordCounts = env.fromElements(
+ new WordWithCount("hello", 1),
+ new WordWithCount("world", 2));
+
+wordCounts.keyBy("word"); // key by field expression "word"
+
+{% endhighlight %}
+
+
+{% highlight scala %}
+class WordWithCount(var word: String, var count: Int) {
+ def this() {
+ this(null, -1)
+ }
+}
+
+val input = env.fromElements(
+ new WordWithCount("hello", 1),
+ new WordWithCount("world", 2)) // Case Class Data Set
+
+input.keyBy("word")// key by field expression "word"
+
+{% endhighlight %}
+
+
+
+#### Primitive Types
+
+Flink supports all Java and Scala primitive types such as `Integer`, `String`, and `Double`.
+
+#### General Class Types
+
+Flink supports most Java and Scala classes (API and custom).
+Restrictions apply to classes containing fields that cannot be serialized, like file pointers, I/O streams, or other native
+resources. Classes that follow the Java Beans conventions work well in general.
+
+All classes that are not identified as POJO types (see POJO requirements above) are handled by Flink as general class types.
+Flink treats these data types as black boxes and is not able to access their content (e.g., for efficient sorting). General types are de/serialized using the serialization framework [Kryo](https://github.com/EsotericSoftware/kryo).
+
+#### Values
+
+*Value* types describe their serialization and deserialization manually. Instead of going through a
+general purpose serialization framework, they provide custom code for those operations by means of
+implementing the `org.apache.flinktypes.Value` interface with the methods `read` and `write`. Using
+a Value type is reasonable when general purpose serialization would be highly inefficient. An
+example would be a data type that implements a sparse vector of elements as an array. Knowing that
+the array is mostly zero, one can use a special encoding for the non-zero elements, while the
+general purpose serialization would simply write all array elements.
+
+The `org.apache.flinktypes.CopyableValue` interface supports manual internal cloning logic in a
+similar way.
+
+Flink comes with pre-defined Value types that correspond to basic data types. (`ByteValue`,
+`ShortValue`, `IntValue`, `LongValue`, `FloatValue`, `DoubleValue`, `StringValue`, `CharValue`,
+`BooleanValue`). These Value types act as mutable variants of the basic data types: Their value can
+be altered, allowing programmers to reuse objects and take pressure off the garbage collector.
+
+
+#### Hadoop Writables
+
+You can use types that implement the `org.apache.hadoop.Writable` interface. The serialization logic
+defined in the `write()`and `readFields()` methods will be used for serialization.
+
+#### Special Types
+
+You can use special types, including Scala's `Either`, `Option`, and `Try`.
+The Java API has its own custom implementation of `Either`.
+Similarly to Scala's `Either`, it represents a value of two possible types, *Left* or *Right*.
+`Either` can be useful for error handling or operators that need to output two different types of records.
+
+#### Type Erasure & Type Inference
+
+*Note: This Section is only relevant for Java.*
+
+The Java compiler throws away much of the generic type information after compilation. This is
+known as *type erasure* in Java. It means that at runtime, an instance of an object does not know
+its generic type any more. For example, instances of `DataStream` and `DataStream` look the
+same to the JVM.
+
+Flink requires type information at the time when it prepares the program for execution (when the
+main method of the program is called). The Flink Java API tries to reconstruct the type information
+that was thrown away in various ways and store it explicitly in the data sets and operators. You can
+retrieve the type via `DataStream.getType()`. The method returns an instance of `TypeInformation`,
+which is Flink's internal way of representing types.
+
+The type inference has its limits and needs the "cooperation" of the programmer in some cases.
+Examples for that are methods that create data sets from collections, such as
+`ExecutionEnvironment.fromCollection(),` where you can pass an argument that describes the type. But
+also generic functions like `MapFunction` may need extra type information.
+
+The
+{% gh_link /flink-core/src/main/java/org/apache/flink/api/java/typeutils/ResultTypeQueryable.java "ResultTypeQueryable" %}
+interface can be implemented by input formats and functions to tell the API
+explicitly about their return type. The *input types* that the functions are invoked with can
+usually be inferred by the result types of the previous operations.
+
+{% top %}
## Flink 中的类型处理
diff --git a/docs/dev/user_defined_functions.zh.md b/docs/dev/user_defined_functions.zh.md
new file mode 100644
index 00000000000000..a1b52a087dc21e
--- /dev/null
+++ b/docs/dev/user_defined_functions.zh.md
@@ -0,0 +1,241 @@
+---
+title: '用户自定义函数'
+nav-id: user_defined_function
+nav-parent_id: streaming
+nav-pos: 4
+---
+
+
+Most operations require a user-defined function. This section lists different
+ways of how they can be specified. We also cover `Accumulators`, which can be
+used to gain insights into your Flink application.
+
+
+
+
+## Implementing an interface
+
+The most basic way is to implement one of the provided interfaces:
+
+{% highlight java %}
+class MyMapFunction implements MapFunction {
+ public Integer map(String value) { return Integer.parseInt(value); }
+};
+data.map(new MyMapFunction());
+{% endhighlight %}
+
+## Anonymous classes
+
+You can pass a function as an anonymous class:
+{% highlight java %}
+data.map(new MapFunction () {
+ public Integer map(String value) { return Integer.parseInt(value); }
+});
+{% endhighlight %}
+
+## Java 8 Lambdas
+
+Flink also supports Java 8 Lambdas in the Java API.
+
+{% highlight java %}
+data.filter(s -> s.startsWith("http://"));
+{% endhighlight %}
+
+{% highlight java %}
+data.reduce((i1,i2) -> i1 + i2);
+{% endhighlight %}
+
+## Rich functions
+
+All transformations that require a user-defined function can
+instead take as argument a *rich* function. For example, instead of
+
+{% highlight java %}
+class MyMapFunction implements MapFunction {
+ public Integer map(String value) { return Integer.parseInt(value); }
+};
+{% endhighlight %}
+
+you can write
+
+{% highlight java %}
+class MyMapFunction extends RichMapFunction {
+ public Integer map(String value) { return Integer.parseInt(value); }
+};
+{% endhighlight %}
+
+and pass the function as usual to a `map` transformation:
+
+{% highlight java %}
+data.map(new MyMapFunction());
+{% endhighlight %}
+
+Rich functions can also be defined as an anonymous class:
+{% highlight java %}
+data.map (new RichMapFunction() {
+ public Integer map(String value) { return Integer.parseInt(value); }
+});
+{% endhighlight %}
+
+
+
+
+
+## Lambda Functions
+
+As already seen in previous examples all operations accept lambda functions for describing
+the operation:
+{% highlight scala %}
+val data: DataSet[String] = // [...]
+data.filter { _.startsWith("http://") }
+{% endhighlight %}
+
+{% highlight scala %}
+val data: DataSet[Int] = // [...]
+data.reduce { (i1,i2) => i1 + i2 }
+// or
+data.reduce { _ + _ }
+{% endhighlight %}
+
+## Rich functions
+
+All transformations that take as argument a lambda function can
+instead take as argument a *rich* function. For example, instead of
+
+{% highlight scala %}
+data.map { x => x.toInt }
+{% endhighlight %}
+
+you can write
+
+{% highlight scala %}
+class MyMapFunction extends RichMapFunction[String, Int] {
+ def map(in: String):Int = { in.toInt }
+};
+{% endhighlight %}
+
+and pass the function to a `map` transformation:
+
+{% highlight scala %}
+data.map(new MyMapFunction())
+{% endhighlight %}
+
+Rich functions can also be defined as an anonymous class:
+{% highlight scala %}
+data.map (new RichMapFunction[String, Int] {
+ def map(in: String):Int = { in.toInt }
+})
+{% endhighlight %}
+
+
+
+
+Rich functions provide, in addition to the user-defined function (map,
+reduce, etc), four methods: `open`, `close`, `getRuntimeContext`, and
+`setRuntimeContext`. These are useful for parameterizing the function
+(see [Passing Parameters to Functions]({{ site.baseurl }}/dev/batch/index.html#passing-parameters-to-functions)),
+creating and finalizing local state, accessing broadcast variables (see
+[Broadcast Variables]({{ site.baseurl }}/dev/batch/index.html#broadcast-variables)), and for accessing runtime
+information such as accumulators and counters (see
+[Accumulators and Counters](#accumulators--counters)), and information
+on iterations (see [Iterations]({{ site.baseurl }}/dev/batch/iterations.html)).
+
+{% top %}
+
+## Accumulators & Counters
+
+Accumulators are simple constructs with an **add operation** and a **final accumulated result**,
+which is available after the job ended.
+
+The most straightforward accumulator is a **counter**: You can increment it using the
+```Accumulator.add(V value)``` method. At the end of the job Flink will sum up (merge) all partial
+results and send the result to the client. Accumulators are useful during debugging or if you
+quickly want to find out more about your data.
+
+Flink currently has the following **built-in accumulators**. Each of them implements the
+{% gh_link /flink-core/src/main/java/org/apache/flink/api/common/accumulators/Accumulator.java "Accumulator" %}
+interface.
+
+- {% gh_link /flink-core/src/main/java/org/apache/flink/api/common/accumulators/IntCounter.java "__IntCounter__" %},
+ {% gh_link /flink-core/src/main/java/org/apache/flink/api/common/accumulators/LongCounter.java "__LongCounter__" %}
+ and {% gh_link /flink-core/src/main/java/org/apache/flink/api/common/accumulators/DoubleCounter.java "__DoubleCounter__" %}:
+ See below for an example using a counter.
+- {% gh_link /flink-core/src/main/java/org/apache/flink/api/common/accumulators/Histogram.java "__Histogram__" %}:
+ A histogram implementation for a discrete number of bins. Internally it is just a map from Integer
+ to Integer. You can use this to compute distributions of values, e.g. the distribution of
+ words-per-line for a word count program.
+
+__How to use accumulators:__
+
+First you have to create an accumulator object (here a counter) in the user-defined transformation
+function where you want to use it.
+
+{% highlight java %}
+private IntCounter numLines = new IntCounter();
+{% endhighlight %}
+
+Second you have to register the accumulator object, typically in the ```open()``` method of the
+*rich* function. Here you also define the name.
+
+{% highlight java %}
+getRuntimeContext().addAccumulator("num-lines", this.numLines);
+{% endhighlight %}
+
+You can now use the accumulator anywhere in the operator function, including in the ```open()``` and
+```close()``` methods.
+
+{% highlight java %}
+this.numLines.add(1);
+{% endhighlight %}
+
+The overall result will be stored in the ```JobExecutionResult``` object which is
+returned from the `execute()` method of the execution environment
+(currently this only works if the execution waits for the
+completion of the job).
+
+{% highlight java %}
+myJobExecutionResult.getAccumulatorResult("num-lines")
+{% endhighlight %}
+
+All accumulators share a single namespace per job. Thus you can use the same accumulator in
+different operator functions of your job. Flink will internally merge all accumulators with the same
+name.
+
+A note on accumulators and iterations: Currently the result of accumulators is only available after
+the overall job has ended. We plan to also make the result of the previous iteration available in the
+next iteration. You can use
+{% gh_link /flink-java/src/main/java/org/apache/flink/api/java/operators/IterativeDataSet.java#L98 "Aggregators" %}
+to compute per-iteration statistics and base the termination of iterations on such statistics.
+
+__Custom accumulators:__
+
+To implement your own accumulator you simply have to write your implementation of the Accumulator
+interface. Feel free to create a pull request if you think your custom accumulator should be shipped
+with Flink.
+
+You have the choice to implement either
+{% gh_link /flink-core/src/main/java/org/apache/flink/api/common/accumulators/Accumulator.java "Accumulator" %}
+or {% gh_link /flink-core/src/main/java/org/apache/flink/api/common/accumulators/SimpleAccumulator.java "SimpleAccumulator" %}.
+
+```Accumulator``` is most flexible: It defines a type ```V``` for the value to add, and a
+result type ```R``` for the final result. E.g. for a histogram, ```V``` is a number and ```R``` is
+ a histogram. ```SimpleAccumulator``` is for the cases where both types are the same, e.g. for counters.
+
+{% top %}
diff --git a/docs/fig/per_split_watermarks.svg b/docs/fig/per_split_watermarks.svg
new file mode 100644
index 00000000000000..8612584977e185
--- /dev/null
+++ b/docs/fig/per_split_watermarks.svg
@@ -0,0 +1,20 @@
+
+
+
diff --git a/docs/fig/processes.svg b/docs/fig/processes.svg
index f2c58fc449d2bd..fe83a9db7c01b0 100644
--- a/docs/fig/processes.svg
+++ b/docs/fig/processes.svg
@@ -233,7 +233,7 @@ under the License.
y="460.43439"
id="text3067"
xml:space="preserve"
- style="font-size:17.55437279px;font-style:normal;font-weight:normal;text-align:start;text-anchor:start;fill:#000000;font-family:Verdana">Flink Master
+ style="font-size:17.55437279px;font-style:normal;font-weight:normal;text-align:start;text-anchor:start;fill:#000000;font-family:Verdana">JobManager
+
+
diff --git a/docs/fig/source_reader.svg b/docs/fig/source_reader.svg
new file mode 100644
index 00000000000000..1d0f3635b3b263
--- /dev/null
+++ b/docs/fig/source_reader.svg
@@ -0,0 +1,20 @@
+
+
+
diff --git a/docs/fig/spend-report-console.png b/docs/fig/spend-report-console.png
new file mode 100644
index 00000000000000..ade38f3f2647a7
Binary files /dev/null and b/docs/fig/spend-report-console.png differ
diff --git a/docs/fig/spend-report-grafana.png b/docs/fig/spend-report-grafana.png
new file mode 100644
index 00000000000000..8cca47f8d37d61
Binary files /dev/null and b/docs/fig/spend-report-grafana.png differ
diff --git a/docs/fig/stream_aligning.svg b/docs/fig/stream_aligning.svg
index de2543597ceb06..2192117b78d152 100644
--- a/docs/fig/stream_aligning.svg
+++ b/docs/fig/stream_aligning.svg
@@ -1,4 +1,4 @@
-
+
-
\ No newline at end of file
diff --git a/docs/fig/stream_unaligning.svg b/docs/fig/stream_unaligning.svg
new file mode 100644
index 00000000000000..8a8b26f313ae07
--- /dev/null
+++ b/docs/fig/stream_unaligning.svg
@@ -0,0 +1,383 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ d
+
+
+
+ d
+
+
+
+
+ 6
+
+
+
+ Operator
+
+
+
+ 5
+
+
+
+
+
+ 4
+
+
+
+ 3
+
+
+
+ 2
+
+
+
+ 1
+
+
+
+ f
+
+
+
+ e
+
+
+
+
+
+ d
+
+
+
+ c
+
+
+
+ b
+
+
+
+ a
+
+
+
+
+ z
+
+
+
+ y
+
+
+
+ x
+
+
+
+ Checkpoint
+ barrier
+
+
+
+ Input
+ buffers
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Output
+ buffers
+
+
+
+
+
+
+
+ Output
+ buffers
+
+
+
+
+ On first barrier
+
+
+
+
+
+
+
+ 6
+
+
+
+ Operator
+
+
+
+ 5
+
+
+
+
+
+ 4
+
+
+
+ 3
+
+
+
+
+
+ 2
+
+
+
+ 1
+
+
+
+ f
+
+
+
+ e
+
+
+
+
+
+ c
+
+
+
+ b
+
+
+
+ a
+
+
+
+ z
+
+
+
+ y
+
+
+
+ x
+
+
+
+ Checkpoint
+ barrier
+
+
+
+ Input
+ buffers
+
+
+
+
+
+
+
+
+
+
+
+ Output
+ buffers
+
+
+
+
+
+
+
+ Output
+ buffers
+
+
+
+ Tag buffers and forward barrier
+
+
+
+
+
+
+ 6
+
+
+
+ Operator
+
+
+
+ 5
+
+
+
+
+
+ 4
+
+
+
+ 3
+
+
+
+
+
+ 2
+
+
+
+ 1
+
+
+
+ f
+
+
+
+ e
+
+
+
+
+
+ c
+
+
+
+ b
+
+
+
+ a
+
+
+
+ z
+
+
+
+ y
+
+
+
+ x
+
+
+
+ Input
+ buffers
+
+
+
+
+
+
+
+
+
+
+
+ Output
+ buffers
+
+
+
+ Checkpoint
+
+
+
+
+ State
+ backend
+
+
+
+
+ state
+
+
+
+
+ state
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ state
+
+
+
\ No newline at end of file
diff --git a/docs/fig/table_connectors.svg b/docs/fig/table_connectors.svg
new file mode 100644
index 00000000000000..f41d0cb9cfc30c
--- /dev/null
+++ b/docs/fig/table_connectors.svg
@@ -0,0 +1,666 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/docs/flinkDev/ide_setup.md b/docs/flinkDev/ide_setup.md
index d944bc688f0864..e715a50c7bfcd7 100644
--- a/docs/flinkDev/ide_setup.md
+++ b/docs/flinkDev/ide_setup.md
@@ -27,8 +27,8 @@ under the License.
The sections below describe how to import the Flink project into an IDE
for the development of Flink itself. For writing Flink programs, please
-refer to the [Java API]({{ site.baseurl }}/dev/projectsetup/java_api_quickstart.html)
-and the [Scala API]({{ site.baseurl }}/dev/projectsetup/scala_api_quickstart.html)
+refer to the [Java API]({{ site.baseurl }}/dev/project-configuration.html)
+and the [Scala API]({{ site.baseurl }}/dev/project-configuration)
quickstart guides.
**NOTE:** Whenever something is not working in your IDE, try with the Maven
diff --git a/docs/flinkDev/ide_setup.zh.md b/docs/flinkDev/ide_setup.zh.md
index 170f26cdf945ab..bdfbc0253ca119 100644
--- a/docs/flinkDev/ide_setup.zh.md
+++ b/docs/flinkDev/ide_setup.zh.md
@@ -27,8 +27,8 @@ under the License.
The sections below describe how to import the Flink project into an IDE
for the development of Flink itself. For writing Flink programs, please
-refer to the [Java API]({{ site.baseurl }}/dev/projectsetup/java_api_quickstart.html)
-and the [Scala API]({{ site.baseurl }}/dev/projectsetup/scala_api_quickstart.html)
+refer to the [Java API]({{ site.baseurl }}/dev/project-configuration.html)
+and the [Scala API]({{ site.baseurl }}/dev/project-configuration)
quickstart guides.
**NOTE:** Whenever something is not working in your IDE, try with the Maven
diff --git a/docs/getting-started/docker-playgrounds/flink-operations-playground.md b/docs/getting-started/docker-playgrounds/flink-operations-playground.md
deleted file mode 100644
index 1e2a569051f2bf..00000000000000
--- a/docs/getting-started/docker-playgrounds/flink-operations-playground.md
+++ /dev/null
@@ -1,828 +0,0 @@
----
-title: "Flink Operations Playground"
-nav-title: 'Flink Operations Playground'
-nav-parent_id: docker-playgrounds
-nav-pos: 1
----
-
-
-There are many ways to deploy and operate Apache Flink in various environments. Regardless of this
-variety, the fundamental building blocks of a Flink Cluster remain the same, and similar
-operational principles apply.
-
-In this playground, you will learn how to manage and run Flink Jobs. You will see how to deploy and
-monitor an application, experience how Flink recovers from Job failure, and perform everyday
-operational tasks like upgrades and rescaling.
-
-{% if site.version contains "SNAPSHOT" %}
-
-
- NOTE: The Apache Flink Docker images used for this playground are only available for
- released versions of Apache Flink.
-
- Since you are currently looking at the latest SNAPSHOT
- version of the documentation, all version references below will not work.
- Please switch the documentation to the latest released version via the release picker which you
- find on the left side below the menu.
-
-{% endif %}
-
-* This will be replaced by the TOC
-{:toc}
-
-## Anatomy of this Playground
-
-This playground consists of a long living
-[Flink Session Cluster]({{ site.baseurl }}/concepts/glossary.html#flink-session-cluster) and a Kafka
-Cluster.
-
-A Flink Cluster always consists of a
-[Flink Master]({{ site.baseurl }}/concepts/glossary.html#flink-master) and one or more
-[Flink TaskManagers]({{ site.baseurl }}/concepts/glossary.html#flink-taskmanager). The Flink Master
-is responsible for handling [Job]({{ site.baseurl }}/concepts/glossary.html#flink-job) submissions,
-the supervision of Jobs as well as resource management. The Flink TaskManagers are the worker
-processes and are responsible for the execution of the actual
-[Tasks]({{ site.baseurl }}/concepts/glossary.html#task) which make up a Flink Job. In this
-playground you will start with a single TaskManager, but scale out to more TaskManagers later.
-Additionally, this playground comes with a dedicated *client* container, which we use to submit the
-Flink Job initially and to perform various operational tasks later on. The *client* container is not
-needed by the Flink Cluster itself but only included for ease of use.
-
-The Kafka Cluster consists of a Zookeeper server and a Kafka Broker.
-
-
-
-When the playground is started a Flink Job called *Flink Event Count* will be submitted to the
-Flink Master. Additionally, two Kafka Topics *input* and *output* are created.
-
-
-
-The Job consumes `ClickEvent`s from the *input* topic, each with a `timestamp` and a `page`. The
-events are then keyed by `page` and counted in 15 second
-[windows]({{ site.baseurl }}/dev/stream/operators/windows.html). The results are written to the
-*output* topic.
-
-There are six different pages and we generate 1000 click events per page and 15 seconds. Hence, the
-output of the Flink job should show 1000 views per page and window.
-
-{% top %}
-
-## Starting the Playground
-
-The playground environment is set up in just a few steps. We will walk you through the necessary
-commands and show how to validate that everything is running correctly.
-
-We assume that you have [Docker](https://docs.docker.com/) (1.12+) and
-[docker-compose](https://docs.docker.com/compose/) (2.1+) installed on your machine.
-
-The required configuration files are available in the
-[flink-playgrounds](https://github.com/apache/flink-playgrounds) repository. Check it out and spin
-up the environment:
-
-{% highlight bash %}
-git clone --branch release-{{ site.version_title }} https://github.com/apache/flink-playgrounds.git
-cd flink-playgrounds/operations-playground
-docker-compose build
-docker-compose up -d
-{% endhighlight %}
-
-Afterwards, you can inspect the running Docker containers with the following command:
-
-{% highlight bash %}
-docker-compose ps
-
- Name Command State Ports
------------------------------------------------------------------------------------------------------------------------------
-operations-playground_clickevent-generator_1 /docker-entrypoint.sh java ... Up 6123/tcp, 8081/tcp
-operations-playground_client_1 /docker-entrypoint.sh flin ... Exit 0
-operations-playground_jobmanager_1 /docker-entrypoint.sh jobm ... Up 6123/tcp, 0.0.0.0:8081->8081/tcp
-operations-playground_kafka_1 start-kafka.sh Up 0.0.0.0:9094->9094/tcp
-operations-playground_taskmanager_1 /docker-entrypoint.sh task ... Up 6123/tcp, 8081/tcp
-operations-playground_zookeeper_1 /bin/sh -c /usr/sbin/sshd ... Up 2181/tcp, 22/tcp, 2888/tcp, 3888/tcp
-{% endhighlight %}
-
-This indicates that the client container has successfully submitted the Flink Job (`Exit 0`) and all
-cluster components as well as the data generator are running (`Up`).
-
-You can stop the playground environment by calling:
-
-{% highlight bash %}
-docker-compose down -v
-{% endhighlight %}
-
-## Entering the Playground
-
-There are many things you can try and check out in this playground. In the following two sections we
-will show you how to interact with the Flink Cluster and demonstrate some of Flink's key features.
-
-### Flink WebUI
-
-The most natural starting point to observe your Flink Cluster is the WebUI exposed under
-[http://localhost:8081](http://localhost:8081). If everything went well, you'll see that the cluster initially consists of
-one TaskManager and executes a Job called *Click Event Count*.
-
-
-
-The Flink WebUI contains a lot of useful and interesting information about your Flink Cluster and
-its Jobs (JobGraph, Metrics, Checkpointing Statistics, TaskManager Status,...).
-
-### Logs
-
-**JobManager**
-
-The JobManager logs can be tailed via `docker-compose`.
-
-{% highlight bash %}
-docker-compose logs -f jobmanager
-{% endhighlight %}
-
-After the initial startup you should mainly see log messages for every checkpoint completion.
-
-**TaskManager**
-
-The TaskManager log can be tailed in the same way.
-{% highlight bash %}
-docker-compose logs -f taskmanager
-{% endhighlight %}
-
-After the initial startup you should mainly see log messages for every checkpoint completion.
-
-### Flink CLI
-
-The [Flink CLI]({{ site.baseurl }}/ops/cli.html) can be used from within the client container. For
-example, to print the `help` message of the Flink CLI you can run
-{% highlight bash%}
-docker-compose run --no-deps client flink --help
-{% endhighlight %}
-
-### Flink REST API
-
-The [Flink REST API]({{ site.baseurl }}/monitoring/rest_api.html#api) is exposed via
-`localhost:8081` on the host or via `jobmanager:8081` from the client container, e.g. to list all
-currently running jobs, you can run:
-{% highlight bash%}
-curl localhost:8081/jobs
-{% endhighlight %}
-
-{% if site.version contains "SNAPSHOT" %}
-
- Note: If the curl command is not available on your machine, you can run it from the client
- container (similar to the Flink CLI):
-{% highlight bash%}
-docker-compose run --no-deps client curl jobmanager:8081/jobs
-{% endhighlight %}
-
-{% endif %}
-
-### Kafka Topics
-
-You can look at the records that are written to the Kafka Topics by running
-{% highlight bash%}
-//input topic (1000 records/s)
-docker-compose exec kafka kafka-console-consumer.sh \
- --bootstrap-server localhost:9092 --topic input
-
-//output topic (24 records/min)
-docker-compose exec kafka kafka-console-consumer.sh \
- --bootstrap-server localhost:9092 --topic output
-{% endhighlight %}
-
-{% top %}
-
-## Time to Play!
-
-Now that you learned how to interact with Flink and the Docker containers, let's have a look at
-some common operational tasks that you can try out on our playground.
-All of these tasks are independent of each other, i.e. you can perform them in any order.
-Most tasks can be executed via the [CLI](#flink-cli) and the [REST API](#flink-rest-api).
-
-### Listing Running Jobs
-
-
-
-The JobID is assigned to a Job upon submission and is needed to perform actions on the Job via the
-CLI or REST API.
-
-### Observing Failure & Recovery
-
-Flink provides exactly-once processing guarantees under (partial) failure. In this playground you
-can observe and - to some extent - verify this behavior.
-
-#### Step 1: Observing the Output
-
-As described [above](#anatomy-of-this-playground), the events in this playground are generate such
-that each window contains exactly one thousand records. So, in order to verify that Flink
-successfully recovers from a TaskManager failure without data loss or duplication you can tail the
-output topic and check that - after recovery - all windows are present and the count is correct.
-
-For this, start reading from the *output* topic and leave this command running until after
-recovery (Step 3).
-
-{% highlight bash%}
-docker-compose exec kafka kafka-console-consumer.sh \
- --bootstrap-server localhost:9092 --topic output
-{% endhighlight %}
-
-#### Step 2: Introducing a Fault
-
-In order to simulate a partial failure you can kill a TaskManager. In a production setup, this
-could correspond to a loss of the TaskManager process, the TaskManager machine or simply a transient
-exception being thrown from the framework or user code (e.g. due to the temporary unavailability of
-an external resource).
-
-{% highlight bash%}
-docker-compose kill taskmanager
-{% endhighlight %}
-
-After a few seconds, the Flink Master will notice the loss of the TaskManager, cancel the affected Job, and
-immediately resubmit it for recovery.
-When the Job gets restarted, its tasks remain in the `SCHEDULED` state, which is indicated by the
-purple colored squares (see screenshot below).
-
-
-
-
- Note: Even though the tasks of the job are in SCHEDULED state and not RUNNING yet, the overall
- status of a Job is shown as RUNNING.
-
-
-At this point, the tasks of the Job cannot move from the `SCHEDULED` state to `RUNNING` because there
-are no resources (TaskSlots provided by TaskManagers) to the run the tasks.
-Until a new TaskManager becomes available, the Job will go through a cycle of cancellations and resubmissions.
-
-In the meantime, the data generator keeps pushing `ClickEvent`s into the *input* topic. This is
-similar to a real production setup where data is produced while the Job to process it is down.
-
-#### Step 3: Recovery
-
-Once you restart the TaskManager, it reconnects to the Master.
-
-{% highlight bash%}
-docker-compose up -d taskmanager
-{% endhighlight %}
-
-When the Master is notified about the new TaskManager, it schedules the tasks of the
-recovering Job to the newly available TaskSlots. Upon restart, the tasks recover their state from
-the last successful [checkpoint]({{ site.baseurl }}/internals/stream_checkpointing.html) that was taken
-before the failure and switch to the `RUNNING` state.
-
-The Job will quickly process the full backlog of input events (accumulated during the outage)
-from Kafka and produce output at a much higher rate (> 24 records/minute) until it reaches
-the head of the stream. In the *output* you will see that all keys (`page`s) are present for all time
-windows and that every count is exactly one thousand. Since we are using the
-[FlinkKafkaProducer]({{ site.baseurl }}/dev/connectors/kafka.html#kafka-producers-and-fault-tolerance)
-in its "at-least-once" mode, there is a chance that you will see some duplicate output records.
-
-
- Note: Most production setups rely on a resource manager (Kubernetes, Yarn, Mesos) to
- automatically restart failed processes.
-
-
-### Upgrading & Rescaling a Job
-
-Upgrading a Flink Job always involves two steps: First, the Flink Job is gracefully stopped with a
-[Savepoint]({{ site.baseurl }}/ops/state/savepoints.html). A Savepoint is a consistent snapshot of
-the complete application state at a well-defined, globally consistent point in time (similar to a
-checkpoint). Second, the upgraded Flink Job is started from the Savepoint. In this context "upgrade"
-can mean different things including the following:
-
-* An upgrade to the configuration (incl. the parallelism of the Job)
-* An upgrade to the topology of the Job (added/removed Operators)
-* An upgrade to the user-defined functions of the Job
-
-Before starting with the upgrade you might want to start tailing the *output* topic, in order to
-observe that no data is lost or corrupted in the course the upgrade.
-
-{% highlight bash%}
-docker-compose exec kafka kafka-console-consumer.sh \
- --bootstrap-server localhost:9092 --topic output
-{% endhighlight %}
-
-#### Step 1: Stopping the Job
-
-To gracefully stop the Job, you need to use the "stop" command of either the CLI or the REST API.
-For this you will need the JobID of the Job, which you can obtain by
-[listing all running Jobs](#listing-running-jobs) or from the WebUI. With the JobID you can proceed
-to stopping the Job:
-
-
-
-**Command**
-{% highlight bash %}
-docker-compose run --no-deps client flink stop
-{% endhighlight %}
-**Expected Output**
-{% highlight bash %}
-Suspending job "" with a savepoint.
-Suspended job "" with a savepoint.
-{% endhighlight %}
-
-The Savepoint has been stored to the `state.savepoint.dir` configured in the *flink-conf.yaml*,
-which is mounted under */tmp/flink-savepoints-directory/* on your local machine. You will need the
-path to this Savepoint in the next step. In case of the REST API this path was already part of the
-response, you will need to have a look at the filesystem directly.
-
-**Command**
-{% highlight bash %}
-ls -lia /tmp/flink-savepoints-directory
-{% endhighlight %}
-
-**Expected Output**
-{% highlight bash %}
-total 0
- 17 drwxr-xr-x 3 root root 60 17 jul 17:05 .
- 2 drwxrwxrwt 135 root root 3420 17 jul 17:09 ..
-1002 drwxr-xr-x 2 root root 140 17 jul 17:05 savepoint--
-{% endhighlight %}
-
-
-#### Step 2a: Restart Job without Changes
-
-You can now restart the upgraded Job from this Savepoint. For simplicity, you can start by
-restarting it without any changes.
-
-
-
-**Command**
-{% highlight bash %}
-docker-compose run --no-deps client flink run -s \
- -d /opt/ClickCountJob.jar \
- --bootstrap.servers kafka:9092 --checkpointing --event-time
-{% endhighlight %}
-**Expected Output**
-{% highlight bash %}
-Starting execution of program
-Job has been submitted with JobID
-{% endhighlight %}
-
-
-Once the Job is `RUNNING` again, you will see in the *output* Topic that records are produced at a
-higher rate while the Job is processing the backlog accumulated during the outage. Additionally,
-you will see that no data was lost during the upgrade: all windows are present with a count of
-exactly one thousand.
-
-#### Step 2b: Restart Job with a Different Parallelism (Rescaling)
-
-Alternatively, you could also rescale the Job from this Savepoint by passing a different parallelism
-during resubmission.
-
-
-
-**Command**
-{% highlight bash %}
-docker-compose run --no-deps client flink run -p 3 -s \
- -d /opt/ClickCountJob.jar \
- --bootstrap.servers kafka:9092 --checkpointing --event-time
-{% endhighlight %}
-**Expected Output**
-{% highlight bash %}
-Starting execution of program
-Job has been submitted with JobID
-{% endhighlight %}
-
-Now, the Job has been resubmitted, but it will not start as there are not enough TaskSlots to
-execute it with the increased parallelism (2 available, 3 needed). With
-{% highlight bash %}
-docker-compose scale taskmanager=2
-{% endhighlight %}
-you can add a second TaskManager with two TaskSlots to the Flink Cluster, which will automatically register with the
-Flink Master. Shortly after adding the TaskManager the Job should start running again.
-
-Once the Job is "RUNNING" again, you will see in the *output* Topic that now data was lost during
-rescaling: all windows are present with a count of exactly one thousand.
-
-### Querying the Metrics of a Job
-
-The Flink Master exposes system and user [metrics]({{ site.baseurl }}/monitoring/metrics.html)
-via its REST API.
-
-The endpoint depends on the scope of these metrics. Metrics scoped to a Job can be listed via
-`jobs//metrics`. The actual value of a metric can be queried via the `get` query parameter.
-
-**Request**
-{% highlight bash %}
-curl "localhost:8081/jobs//metrics?get=lastCheckpointSize"
-{% endhighlight %}
-**Expected Response (pretty-printed; no placeholders)**
-{% highlight json %}
-[
- {
- "id": "lastCheckpointSize",
- "value": "9378"
- }
-]
-{% endhighlight %}
-
-The REST API can not only be used to query metrics, but you can also retrieve detailed information
-about the status of a running Job.
-
-**Request**
-{% highlight bash %}
-# find the vertex-id of the vertex of interest
-curl localhost:8081/jobs/
-{% endhighlight %}
-
-**Expected Response (pretty-printed)**
-{% highlight json %}
-{
- "jid": "",
- "name": "Click Event Count",
- "isStoppable": false,
- "state": "RUNNING",
- "start-time": 1564467066026,
- "end-time": -1,
- "duration": 374793,
- "now": 1564467440819,
- "timestamps": {
- "CREATED": 1564467066026,
- "FINISHED": 0,
- "SUSPENDED": 0,
- "FAILING": 0,
- "CANCELLING": 0,
- "CANCELED": 0,
- "RECONCILING": 0,
- "RUNNING": 1564467066126,
- "FAILED": 0,
- "RESTARTING": 0
- },
- "vertices": [
- {
- "id": "",
- "name": "ClickEvent Source",
- "parallelism": 2,
- "status": "RUNNING",
- "start-time": 1564467066423,
- "end-time": -1,
- "duration": 374396,
- "tasks": {
- "CREATED": 0,
- "FINISHED": 0,
- "DEPLOYING": 0,
- "RUNNING": 2,
- "CANCELING": 0,
- "FAILED": 0,
- "CANCELED": 0,
- "RECONCILING": 0,
- "SCHEDULED": 0
- },
- "metrics": {
- "read-bytes": 0,
- "read-bytes-complete": true,
- "write-bytes": 5033461,
- "write-bytes-complete": true,
- "read-records": 0,
- "read-records-complete": true,
- "write-records": 166351,
- "write-records-complete": true
- }
- },
- {
- "id": "",
- "name": "Timestamps/Watermarks",
- "parallelism": 2,
- "status": "RUNNING",
- "start-time": 1564467066441,
- "end-time": -1,
- "duration": 374378,
- "tasks": {
- "CREATED": 0,
- "FINISHED": 0,
- "DEPLOYING": 0,
- "RUNNING": 2,
- "CANCELING": 0,
- "FAILED": 0,
- "CANCELED": 0,
- "RECONCILING": 0,
- "SCHEDULED": 0
- },
- "metrics": {
- "read-bytes": 5066280,
- "read-bytes-complete": true,
- "write-bytes": 5033496,
- "write-bytes-complete": true,
- "read-records": 166349,
- "read-records-complete": true,
- "write-records": 166349,
- "write-records-complete": true
- }
- },
- {
- "id": "",
- "name": "ClickEvent Counter",
- "parallelism": 2,
- "status": "RUNNING",
- "start-time": 1564467066469,
- "end-time": -1,
- "duration": 374350,
- "tasks": {
- "CREATED": 0,
- "FINISHED": 0,
- "DEPLOYING": 0,
- "RUNNING": 2,
- "CANCELING": 0,
- "FAILED": 0,
- "CANCELED": 0,
- "RECONCILING": 0,
- "SCHEDULED": 0
- },
- "metrics": {
- "read-bytes": 5085332,
- "read-bytes-complete": true,
- "write-bytes": 316,
- "write-bytes-complete": true,
- "read-records": 166305,
- "read-records-complete": true,
- "write-records": 6,
- "write-records-complete": true
- }
- },
- {
- "id": "",
- "name": "ClickEventStatistics Sink",
- "parallelism": 2,
- "status": "RUNNING",
- "start-time": 1564467066476,
- "end-time": -1,
- "duration": 374343,
- "tasks": {
- "CREATED": 0,
- "FINISHED": 0,
- "DEPLOYING": 0,
- "RUNNING": 2,
- "CANCELING": 0,
- "FAILED": 0,
- "CANCELED": 0,
- "RECONCILING": 0,
- "SCHEDULED": 0
- },
- "metrics": {
- "read-bytes": 20668,
- "read-bytes-complete": true,
- "write-bytes": 0,
- "write-bytes-complete": true,
- "read-records": 6,
- "read-records-complete": true,
- "write-records": 0,
- "write-records-complete": true
- }
- }
- ],
- "status-counts": {
- "CREATED": 0,
- "FINISHED": 0,
- "DEPLOYING": 0,
- "RUNNING": 4,
- "CANCELING": 0,
- "FAILED": 0,
- "CANCELED": 0,
- "RECONCILING": 0,
- "SCHEDULED": 0
- },
- "plan": {
- "jid": "",
- "name": "Click Event Count",
- "nodes": [
- {
- "id": "",
- "parallelism": 2,
- "operator": "",
- "operator_strategy": "",
- "description": "ClickEventStatistics Sink",
- "inputs": [
- {
- "num": 0,
- "id": "",
- "ship_strategy": "FORWARD",
- "exchange": "pipelined_bounded"
- }
- ],
- "optimizer_properties": {}
- },
- {
- "id": "",
- "parallelism": 2,
- "operator": "",
- "operator_strategy": "",
- "description": "ClickEvent Counter",
- "inputs": [
- {
- "num": 0,
- "id": "",
- "ship_strategy": "HASH",
- "exchange": "pipelined_bounded"
- }
- ],
- "optimizer_properties": {}
- },
- {
- "id": "",
- "parallelism": 2,
- "operator": "",
- "operator_strategy": "",
- "description": "Timestamps/Watermarks",
- "inputs": [
- {
- "num": 0,
- "id": "",
- "ship_strategy": "FORWARD",
- "exchange": "pipelined_bounded"
- }
- ],
- "optimizer_properties": {}
- },
- {
- "id": "",
- "parallelism": 2,
- "operator": "",
- "operator_strategy": "",
- "description": "ClickEvent Source",
- "optimizer_properties": {}
- }
- ]
- }
-}
-{% endhighlight %}
-
-Please consult the [REST API reference]({{ site.baseurl }}/monitoring/rest_api.html#api)
-for a complete list of possible queries including how to query metrics of different scopes (e.g.
-TaskManager metrics);
-
-{% top %}
-
-## Variants
-
-You might have noticed that the *Click Event Count* application was always started with `--checkpointing`
-and `--event-time` program arguments. By omitting these in the command of the *client* container in the
-`docker-compose.yaml`, you can change the behavior of the Job.
-
-* `--checkpointing` enables [checkpoint]({{ site.baseurl }}/internals/stream_checkpointing.html),
-which is Flink's fault-tolerance mechanism. If you run without it and go through
-[failure and recovery](#observing-failure--recovery), you should will see that data is actually
-lost.
-
-* `--event-time` enables [event time semantics]({{ site.baseurl }}/dev/event_time.html) for your
-Job. When disabled, the Job will assign events to windows based on the wall-clock time instead of
-the timestamp of the `ClickEvent`. Consequently, the number of events per window will not be exactly
-one thousand anymore.
-
-The *Click Event Count* application also has another option, turned off by default, that you can
-enable to explore the behavior of this job under backpressure. You can add this option in the
-command of the *client* container in `docker-compose.yaml`.
-
-* `--backpressure` adds an additional operator into the middle of the job that causes severe backpressure
-during even-numbered minutes (e.g., during 10:12, but not during 10:13). This can be observed by
-inspecting various [network metrics]({{ site.baseurl }}/monitoring/metrics.html#default-shuffle-service)
-such as `outputQueueLength` and `outPoolUsage`, and/or by using the
-[backpressure monitoring]({{ site.baseurl }}/monitoring/back_pressure.html#monitoring-back-pressure)
-available in the WebUI.
diff --git a/docs/getting-started/docker-playgrounds/index.md b/docs/getting-started/docker-playgrounds/index.md
deleted file mode 100644
index e3d5fd87a3a488..00000000000000
--- a/docs/getting-started/docker-playgrounds/index.md
+++ /dev/null
@@ -1,25 +0,0 @@
----
-title: Docker Playgrounds
-nav-id: docker-playgrounds
-nav-title: 'Docker Playgrounds'
-nav-parent_id: getting-started
-nav-pos: 10
----
-
diff --git a/docs/getting-started/docker-playgrounds/index.zh.md b/docs/getting-started/docker-playgrounds/index.zh.md
deleted file mode 100644
index e3d5fd87a3a488..00000000000000
--- a/docs/getting-started/docker-playgrounds/index.zh.md
+++ /dev/null
@@ -1,25 +0,0 @@
----
-title: Docker Playgrounds
-nav-id: docker-playgrounds
-nav-title: 'Docker Playgrounds'
-nav-parent_id: getting-started
-nav-pos: 10
----
-
diff --git a/docs/getting-started/index.md b/docs/getting-started/index.md
deleted file mode 100644
index fffc502aee58e6..00000000000000
--- a/docs/getting-started/index.md
+++ /dev/null
@@ -1,87 +0,0 @@
----
-title: "Getting Started"
-nav-id: getting-started
-nav-title: ' Getting Started'
-nav-parent_id: root
-section-break: true
-nav-show_overview: true
-nav-pos: 1
-always-expand: true
----
-
-
-There are many ways to get started with Apache Flink. Which one is the best for
-you depends on your goals and prior experience:
-
-* take a look at the **Docker Playgrounds** if you want to see what Flink can do, via a hands-on,
- docker-based introduction to specific Flink concepts
-* explore one of the **Code Walkthroughs** if you want a quick, end-to-end
- introduction to one of Flink's APIs
-* work your way through the **Hands-on Training** for a comprehensive,
- step-by-step introduction to Flink
-* use **Project Setup** if you already know the basics of Flink and want a
- project template for Java or Scala, or need help setting up the dependencies
-
-### Taking a first look at Flink
-
-The **Docker Playgrounds** provide sandboxed Flink environments that are set up in just a few minutes and which allow you to explore and play with Flink.
-
-* The [**Operations Playground**]({% link getting-started/docker-playgrounds/flink-operations-playground.md %}) shows you how to operate streaming applications with Flink. You can experience how Flink recovers application from failures, upgrade and scale streaming applications up and down, and query application metrics.
-
-
-
-### First steps with one of Flink's APIs
-
-The **Code Walkthroughs** are a great way to get started quickly with a step-by-step introduction to
-one of Flink's APIs. Each walkthrough provides instructions for bootstrapping a small skeleton
-project, and then shows how to extend it to a simple application.
-
-* The [**DataStream API** code walkthrough]({% link getting-started/walkthroughs/datastream_api.md %}) shows how
- to implement a simple DataStream application and how to extend it to be stateful and use timers.
- The DataStream API is Flink's main abstraction for implementing stateful streaming applications
- with sophisticated time semantics in Java or Scala.
-
-* Flink's **Table API** is a relational API used for writing SQL-like queries in Java, Scala, or
- Python, which are then automatically optimized, and can be executed on batch or streaming data
- with identical syntax and semantics. The [Table API code walkthrough for Java and Scala]({% link
- getting-started/walkthroughs/table_api.md %}) shows how to implement a simple Table API query on a
- batch source and how to evolve it into a continuous query on a streaming source. There's also a
- similar [code walkthrough for the Python Table API]({% link
- getting-started/walkthroughs/python_table_api.md %}).
-
-### Taking a Deep Dive with the Hands-on Training
-
-The [**Hands-on Training**]({% link training/index.md %}) is a self-paced training course with
-a set of lessons and hands-on exercises. This step-by-step introduction to Flink focuses
-on learning how to use the DataStream API to meet the needs of common, real-world use cases,
-and provides a complete introduction to the fundamental concepts: parallel dataflows,
-stateful stream processing, event time and watermarking, and fault tolerance via state snapshots.
-
-
diff --git a/docs/getting-started/index.zh.md b/docs/getting-started/index.zh.md
deleted file mode 100644
index cb81bbc8bab041..00000000000000
--- a/docs/getting-started/index.zh.md
+++ /dev/null
@@ -1,62 +0,0 @@
----
-title: 入门指南
-nav-id: getting-started
-nav-title: ' Getting Started'
-nav-parent_id: root
-section-break: true
-nav-show_overview: true
-nav-pos: 1
-always-expand: true
----
-
-
-上手使用 Apache Flink 有很多方式,哪一个最适合你取决于你的目标和以前的经验。
-
-### 初识 Flink
-
-通过 **Docker Playgrounds** 提供沙箱的Flink环境,你只需花几分钟做些简单设置,就可以开始探索和使用 Flink。
-
-* [**Operations Playground**](./docker-playgrounds/flink-operations-playground.html) 向你展示如何使用 Flink 编写数据流应用程序。你可以体验 Flink 如何从故障中恢复应用程序,升级、提高并行度、降低并行度和监控运行的状态指标等特性。
-
-
-
-### Flink API 入门
-
-**代码练习**是入门的最佳方式,通过代码练习可以逐步深入理解 Flink API。
-下边的例子演示了如何使用 Flink 的代码框架开始构建一个基础的 Flink 项目,和如何逐步将其扩展为一个简单的应用程序。
-
-
-* [**DataStream API 示例**](./walkthroughs/datastream_api.html) 展示了如何编写一个基本的 DataStream 应用程序。 DataStream API 是 Flink 的主要抽象,用于通过 Java 或 Scala 实现具有复杂时间语义的有状态数据流处理的应用程序。
-
-* [**Table API 示例**](./walkthroughs/table_api.html) 演示了如何在批处中使用简单的 Table API 进行查询,以及如何将其扩展为流处理中的查询。Table API 是 Flink 的语言嵌入式关系 API,用于在 Java 或 Scala 中编写类 SQL 的查询,这些查询会自动进行优化。Table API 查询可以使用一致的语法和语义同时在批处理或流数据上运行。
-
-
diff --git a/docs/getting-started/project-setup/dependencies.md b/docs/getting-started/project-setup/dependencies.md
deleted file mode 100644
index 51f1dd020d22d3..00000000000000
--- a/docs/getting-started/project-setup/dependencies.md
+++ /dev/null
@@ -1,237 +0,0 @@
----
-title: "Configuring Dependencies, Connectors, Libraries"
-nav-parent_id: project-setup
-nav-pos: 2
----
-
-
-Every Flink application depends on a set of Flink libraries. At the bare minimum, the application depends
-on the Flink APIs. Many applications depend in addition on certain connector libraries (like Kafka, Cassandra, etc.).
-When running Flink applications (either in a distributed deployment, or in the IDE for testing), the Flink
-runtime library must be available as well.
-
-
-## Flink Core and Application Dependencies
-
-As with most systems that run user-defined applications, there are two broad categories of dependencies and libraries in Flink:
-
- - **Flink Core Dependencies**: Flink itself consists of a set of classes and dependencies that are needed to run the system, for example
- coordination, networking, checkpoints, failover, APIs, operations (such as windowing), resource management, etc.
- The set of all these classes and dependencies forms the core of Flink's runtime and must be present when a Flink
- application is started.
-
- These core classes and dependencies are packaged in the `flink-dist` jar. They are part of Flink's `lib` folder and
- part of the basic Flink container images. Think of these dependencies as similar to Java's core library (`rt.jar`, `charsets.jar`, etc.),
- which contains the classes like `String` and `List`.
-
- The Flink Core Dependencies do not contain any connectors or libraries (CEP, SQL, ML, etc.) in order to avoid having an excessive
- number of dependencies and classes in the classpath by default. In fact, we try to keep the core dependencies as slim as possible
- to keep the default classpath small and avoid dependency clashes.
-
- - The **User Application Dependencies** are all connectors, formats, or libraries that a specific user application needs.
-
- The user application is typically packaged into an *application jar*, which contains the application code and the required
- connector and library dependencies.
-
- The user application dependencies explicitly do not include the Flink DataSet / DataStream APIs and runtime dependencies,
- because those are already part of Flink's Core Dependencies.
-
-
-## Setting up a Project: Basic Dependencies
-
-Every Flink application needs as the bare minimum the API dependencies, to develop against.
-For Maven, you can use the [Java Project Template]({{ site.baseurl }}/dev/projectsetup/java_api_quickstart.html)
-or [Scala Project Template]({{ site.baseurl }}/dev/projectsetup/scala_api_quickstart.html) to create
-a program skeleton with these initial dependencies.
-
-When setting up a project manually, you need to add the following dependencies for the Java/Scala API
-(here presented in Maven syntax, but the same dependencies apply to other build tools (Gradle, SBT, etc.) as well.
-
-
-
-**Important:** Please note that all these dependencies have their scope set to *provided*.
-That means that they are needed to compile against, but that they should not be packaged into the
-project's resulting application jar file - these dependencies are Flink Core Dependencies,
-which are already available in any setup.
-
-It is highly recommended to keep the dependencies in scope *provided*. If they are not set to *provided*,
-the best case is that the resulting JAR becomes excessively large, because it also contains all Flink core
-dependencies. The worst case is that the Flink core dependencies that are added to the application's jar file
-clash with some of your own dependency versions (which is normally avoided through inverted classloading).
-
-**Note on IntelliJ:** To make the applications run within IntelliJ IDEA it is necessary to tick the
-`Include dependencies with "Provided" scope` box in the run configuration.
-If this option is not available (possibly due to using an older IntelliJ IDEA version), then a simple workaround
-is to create a test that calls the applications `main()` method.
-
-
-## Adding Connector and Library Dependencies
-
-Most applications need specific connectors or libraries to run, for example a connector to Kafka, Cassandra, etc.
-These connectors are not part of Flink's core dependencies and must hence be added as dependencies to the application
-
-Below is an example adding the connector for Kafka 0.10 as a dependency (Maven syntax):
-{% highlight xml %}
-
- org.apache.flink
- flink-connector-kafka-0.10{{ site.scala_version_suffix }}
- {{site.version }}
-
-{% endhighlight %}
-
-We recommend to package the application code and all its required dependencies into one *jar-with-dependencies* which
-we refer to as the *application jar*. The application jar can be submitted to an already running Flink cluster,
-or added to a Flink application container image.
-
-Projects created from the [Java Project Template]({{ site.baseurl }}/dev/projectsetup/java_api_quickstart.html) or
-[Scala Project Template]({{ site.baseurl }}/dev/projectsetup/scala_api_quickstart.html) are configured to automatically include
-the application dependencies into the application jar when running `mvn clean package`. For projects that are
-not set up from those templates, we recommend to add the Maven Shade Plugin (as listed in the Appendix below)
-to build the application jar with all required dependencies.
-
-**Important:** For Maven (and other build tools) to correctly package the dependencies into the application jar,
-these application dependencies must be specified in scope *compile* (unlike the core dependencies, which
-must be specified in scope *provided*).
-
-
-## Scala Versions
-
-Scala versions (2.10, 2.11, 2.12, etc.) are not binary compatible with one another.
-For that reason, Flink for Scala 2.11 cannot be used with an application that uses
-Scala 2.12.
-
-All Flink dependencies that (transitively) depend on Scala are suffixed with the
-Scala version that they are built for, for example `flink-streaming-scala_2.11`.
-
-Developers that only use Java can pick any Scala version, Scala developers need to
-pick the Scala version that matches their application's Scala version.
-
-Please refer to the [build guide]({{ site.baseurl }}/flinkDev/building.html#scala-versions)
-for details on how to build Flink for a specific Scala version.
-
-## Hadoop Dependencies
-
-**General rule: It should never be necessary to add Hadoop dependencies directly to your application.**
-*(The only exception being when using existing Hadoop input-/output formats with Flink's Hadoop compatibility wrappers)*
-
-If you want to use Flink with Hadoop, you need to have a Flink setup that includes the Hadoop dependencies, rather than
-adding Hadoop as an application dependency. Please refer to the [Hadoop Setup Guide]({{ site.baseurl }}/ops/deployment/hadoop.html)
-for details.
-
-There are two main reasons for that design:
-
- - Some Hadoop interaction happens in Flink's core, possibly before the user application is started, for example
- setting up HDFS for checkpoints, authenticating via Hadoop's Kerberos tokens, or deployment on YARN.
-
- - Flink's inverted classloading approach hides many transitive dependencies from the core dependencies. That applies not only
- to Flink's own core dependencies, but also to Hadoop's dependencies when present in the setup.
- That way, applications can use different versions of the same dependencies without running into dependency conflicts (and
- trust us, that's a big deal, because Hadoops dependency tree is huge.)
-
-If you need Hadoop dependencies during testing or development inside the IDE (for example for HDFS access), please configure
-these dependencies similar to the scope of the dependencies to *test* or to *provided*.
-
-
-## Appendix: Template for building a Jar with Dependencies
-
-To build an application JAR that contains all dependencies required for declared connectors and libraries,
-you can use the following shade plugin definition:
-
-{% highlight xml %}
-
-
-
- org.apache.maven.plugins
- maven-shade-plugin
- 3.1.1
-
-
- package
-
- shade
-
-
-
-
- com.google.code.findbugs:jsr305
- org.slf4j:*
- log4j:*
-
-
-
-
-
- *:*
-
- META-INF/*.SF
- META-INF/*.DSA
- META-INF/*.RSA
-
-
-
-
-
- my.programs.main.clazz
-
-
-
-
-
-
-
-
-{% endhighlight %}
-
-{% top %}
-
diff --git a/docs/getting-started/project-setup/dependencies.zh.md b/docs/getting-started/project-setup/dependencies.zh.md
deleted file mode 100644
index 310000fb9ce854..00000000000000
--- a/docs/getting-started/project-setup/dependencies.zh.md
+++ /dev/null
@@ -1,200 +0,0 @@
----
-title: "配置依赖、连接器、类库"
-nav-parent_id: project-setup
-nav-pos: 2
----
-
-
-每个 Flink 应用都需要依赖一组 Flink 类库。Flink 应用至少需要依赖 Flink APIs。许多应用还会额外依赖连接器类库(比如 Kafka、Cassandra 等)。
-当用户运行 Flink 应用时(无论是在 IDE 环境下进行测试,还是部署在分布式环境下),运行时类库都必须可用。
-
-## Flink 核心依赖以及应用依赖
-
-与其他运行用户自定义应用的大多数系统一样,Flink 中有两大类依赖类库
-
- - **Flink 核心依赖**:Flink 本身包含运行所需的一组类和依赖,比如协调、网络通讯、checkpoint、容错处理、API、算子(如窗口操作)、
- 资源管理等,这些类和依赖形成了 Flink 运行时的核心。当 Flink 应用启动时,这些依赖必须可用。
-
- 这些核心类和依赖被打包在 `flink-dist` jar 里。它们是 Flink `lib` 文件夹下的一部分,也是 Flink 基本容器镜像的一部分。
- 这些依赖类似 Java `String` 和 `List` 的核心类库(`rt.jar`, `charsets.jar`等)。
-
- Flink 核心依赖不包含连接器和类库(如 CEP、SQL、ML 等),这样做的目的是默认情况下避免在类路径中具有过多的依赖项和类。
- 实际上,我们希望尽可能保持核心依赖足够精简,以保证一个较小的默认类路径,并且避免依赖冲突。
-
- - **用户应用依赖** 是指特定的应用程序需要的类库,如连接器,formats等。
-
- 用户应用代码和所需的连接器以及其他类库依赖通常被打包到 *application jar* 中。
-
- 用户应用程序依赖项不需包括 Flink DataSet / DataStream API 以及运行时依赖项,因为它们已经是 Flink 核心依赖项的一部分。
-
-## 搭建一个项目: 基础依赖
-
-开发 Flink 应用程序需要最低限度的 API 依赖。Maven 用户,可以使用
-[Java 项目模板]({{ site.baseurl }}/zh/dev/projectsetup/java_api_quickstart.html)或者
-[Scala 项目模板]({{ site.baseurl }}/zh/dev/projectsetup/scala_api_quickstart.html)来创建一个包含最初依赖的程序骨架。
-
-手动设置项目时,需要为 Java 或 Scala API 添加以下依赖项(这里以 Maven 语法为例,但也适用于其他构建工具(Gradle、 SBT 等))。
-
-
-{% highlight bash %}
-$ mvn archetype:generate \
- -DarchetypeGroupId=org.apache.flink \
- -DarchetypeArtifactId=flink-quickstart-java \{% unless site.is_stable %}
- -DarchetypeCatalog=https://repository.apache.org/content/repositories/snapshots/ \{% endunless %}
- -DarchetypeVersion={{site.version}}
-{% endhighlight %}
- This allows you to name your newly created project. It will interactively ask you for the groupId, artifactId, and package name.
-
- Note: For Maven 3.0 or higher, it is no longer possible to specify the repository (-DarchetypeCatalog) via the command line. For details about this change, please refer to Maven official document
- If you wish to use the snapshot repository, you need to add a repository entry to your settings.xml. For example:
-{% highlight bash %}
-
-
- apache
-
-
-
- apache
-
-
- apache-snapshots
- https://repository.apache.org/content/repositories/snapshots/
-
-
-
-
-
-{% endhighlight %}
-
- {% endunless %}
-
-
-### Inspect Project
-
-There will be a new directory in your working directory. If you've used
-the _curl_ approach, the directory is called `quickstart`. Otherwise,
-it has the name of your `artifactId`:
-
-{% highlight bash %}
-$ tree quickstart/
-quickstart/
-├── pom.xml
-└── src
- └── main
- ├── java
- │ └── org
- │ └── myorg
- │ └── quickstart
- │ ├── BatchJob.java
- │ └── StreamingJob.java
- └── resources
- └── log4j2.properties
-{% endhighlight %}
-
-The sample project is a __Maven project__, which contains two classes: _StreamingJob_ and _BatchJob_ are the basic skeleton programs for a *DataStream* and *DataSet* program.
-The _main_ method is the entry point of the program, both for in-IDE testing/execution and for proper deployments.
-
-We recommend you __import this project into your IDE__ to develop and
-test it. IntelliJ IDEA supports Maven projects out of the box.
-If you use Eclipse, the [m2e plugin](http://www.eclipse.org/m2e/)
-allows to [import Maven projects](http://books.sonatype.com/m2eclipse-book/reference/creating-sect-importing-projects.html#fig-creating-import).
-Some Eclipse bundles include that plugin by default, others require you
-to install it manually.
-
-*Please note*: The default JVM heapsize for Java may be too
-small for Flink. You have to manually increase it.
-In Eclipse, choose `Run Configurations -> Arguments` and write into the `VM Arguments` box: `-Xmx800m`.
-In IntelliJ IDEA recommended way to change JVM options is from the `Help | Edit Custom VM Options` menu. See [this article](https://intellij-support.jetbrains.com/hc/en-us/articles/206544869-Configuring-JVM-options-and-platform-properties) for details.
-
-### Build Project
-
-If you want to __build/package your project__, go to your project directory and
-run the '`mvn clean package`' command.
-You will __find a JAR file__ that contains your application, plus connectors and libraries
-that you may have added as dependencies to the application: `target/-.jar`.
-
-__Note:__ If you use a different class than *StreamingJob* as the application's main class / entry point,
-we recommend you change the `mainClass` setting in the `pom.xml` file accordingly. That way, Flink
-can run the application from the JAR file without additionally specifying the main class.
-
-## Gradle
-
-### Requirements
-
-The only requirements are working __Gradle 3.x__ (or higher) and __Java 8.x__ installations.
-
-### Create Project
-
-Use one of the following commands to __create a project__:
-
-
-{% highlight gradle %}
-buildscript {
- repositories {
- jcenter() // this applies only to the Gradle 'Shadow' plugin
- }
- dependencies {
- classpath 'com.github.jengelman.gradle.plugins:shadow:2.0.4'
- }
-}
-
-plugins {
- id 'java'
- id 'application'
- // shadow plugin to produce fat JARs
- id 'com.github.johnrengelman.shadow' version '2.0.4'
-}
-
-
-// artifact properties
-group = 'org.myorg.quickstart'
-version = '0.1-SNAPSHOT'
-mainClassName = 'org.myorg.quickstart.StreamingJob'
-description = """Flink Quickstart Job"""
-
-ext {
- javaVersion = '1.8'
- flinkVersion = '{{ site.version }}'
- scalaBinaryVersion = '{{ site.scala_version }}'
- slf4jVersion = '1.7.15'
- log4jVersion = '2.12.1'
-}
-
-
-sourceCompatibility = javaVersion
-targetCompatibility = javaVersion
-tasks.withType(JavaCompile) {
- options.encoding = 'UTF-8'
-}
-
-applicationDefaultJvmArgs = ["-Dlog4j.configurationFile=log4j2.properties"]
-
-task wrapper(type: Wrapper) {
- gradleVersion = '3.1'
-}
-
-// declare where to find the dependencies of your project
-repositories {
- mavenCentral()
- maven { url "https://repository.apache.org/content/repositories/snapshots/" }
-}
-
-// NOTE: We cannot use "compileOnly" or "shadow" configurations since then we could not run code
-// in the IDE or with "gradle run". We also cannot exclude transitive dependencies from the
-// shadowJar yet (see https://github.com/johnrengelman/shadow/issues/159).
-// -> Explicitly define the // libraries we want to be included in the "flinkShadowJar" configuration!
-configurations {
- flinkShadowJar // dependencies which go into the shadowJar
-
- // always exclude these (also from transitive dependencies) since they are provided by Flink
- flinkShadowJar.exclude group: 'org.apache.flink', module: 'force-shading'
- flinkShadowJar.exclude group: 'com.google.code.findbugs', module: 'jsr305'
- flinkShadowJar.exclude group: 'org.slf4j'
- flinkShadowJar.exclude group: 'org.apache.logging.log4j'
-}
-
-// declare the dependencies for your production and test code
-dependencies {
- // --------------------------------------------------------------
- // Compile-time dependencies that should NOT be part of the
- // shadow jar and are provided in the lib folder of Flink
- // --------------------------------------------------------------
- compile "org.apache.flink:flink-java:${flinkVersion}"
- compile "org.apache.flink:flink-streaming-java_${scalaBinaryVersion}:${flinkVersion}"
-
- // --------------------------------------------------------------
- // Dependencies that should be part of the shadow jar, e.g.
- // connectors. These must be in the flinkShadowJar configuration!
- // --------------------------------------------------------------
- //flinkShadowJar "org.apache.flink:flink-connector-kafka-0.11_${scalaBinaryVersion}:${flinkVersion}"
-
- compile "org.apache.logging.log4j:log4j-api:${log4jVersion}"
- compile "org.apache.logging.log4j:log4j-core:${log4jVersion}"
- compile "org.apache.logging.log4j:log4j-slf4j-impl:${log4jVersion}"
- compile "org.slf4j:slf4j-log4j12:${slf4jVersion}"
-
- // Add test dependencies here.
- // testCompile "junit:junit:4.12"
-}
-
-// make compileOnly dependencies available for tests:
-sourceSets {
- main.compileClasspath += configurations.flinkShadowJar
- main.runtimeClasspath += configurations.flinkShadowJar
-
- test.compileClasspath += configurations.flinkShadowJar
- test.runtimeClasspath += configurations.flinkShadowJar
-
- javadoc.classpath += configurations.flinkShadowJar
-}
-
-run.classpath = sourceSets.main.runtimeClasspath
-
-jar {
- manifest {
- attributes 'Built-By': System.getProperty('user.name'),
- 'Build-Jdk': System.getProperty('java.version')
- }
-}
-
-shadowJar {
- configurations = [project.configurations.flinkShadowJar]
-}
-{% endhighlight %}
-
-{% highlight bash %}
-bash -c "$(curl https://flink.apache.org/q/gradle-quickstart.sh)" -- {{site.version}} {{site.scala_version}}
-{% endhighlight %}
- This allows you to name your newly created project. It will interactively ask
- you for the project name, organization (also used for the package name), project version,
- Scala and Flink version.
-
-
-
-### Inspect Project
-
-There will be a new directory in your working directory based on the
-project name you provided, e.g. for `quickstart`:
-
-{% highlight bash %}
-$ tree quickstart/
-quickstart/
-├── README
-├── build.gradle
-├── settings.gradle
-└── src
- └── main
- ├── java
- │ └── org
- │ └── myorg
- │ └── quickstart
- │ ├── BatchJob.java
- │ └── StreamingJob.java
- └── resources
- └── log4j2.properties
-{% endhighlight %}
-
-The sample project is a __Gradle project__, which contains two classes: _StreamingJob_ and _BatchJob_ are the basic skeleton programs for a *DataStream* and *DataSet* program.
-The _main_ method is the entry point of the program, both for in-IDE testing/execution and for proper deployments.
-
-We recommend you __import this project into your IDE__ to develop and
-test it. IntelliJ IDEA supports Gradle projects after installing the `Gradle` plugin.
-Eclipse does so via the [Eclipse Buildship](https://projects.eclipse.org/projects/tools.buildship) plugin
-(make sure to specify a Gradle version >= 3.0 in the last step of the import wizard; the `shadow` plugin requires it).
-You may also use [Gradle's IDE integration](https://docs.gradle.org/current/userguide/userguide.html#ide-integration)
-to create project files from Gradle.
-
-
-*Please note*: The default JVM heapsize for Java may be too
-small for Flink. You have to manually increase it.
-In Eclipse, choose `Run Configurations -> Arguments` and write into the `VM Arguments` box: `-Xmx800m`.
-In IntelliJ IDEA recommended way to change JVM options is from the `Help | Edit Custom VM Options` menu. See [this article](https://intellij-support.jetbrains.com/hc/en-us/articles/206544869-Configuring-JVM-options-and-platform-properties) for details.
-
-### Build Project
-
-If you want to __build/package your project__, go to your project directory and
-run the '`gradle clean shadowJar`' command.
-You will __find a JAR file__ that contains your application, plus connectors and libraries
-that you may have added as dependencies to the application: `build/libs/--all.jar`.
-
-__Note:__ If you use a different class than *StreamingJob* as the application's main class / entry point,
-we recommend you change the `mainClassName` setting in the `build.gradle` file accordingly. That way, Flink
-can run the application from the JAR file without additionally specifying the main class.
-
-## Next Steps
-
-Write your application!
-
-If you are writing a streaming application and you are looking for inspiration what to write,
-take a look at the [Stream Processing Application Tutorial]({{ site.baseurl }}/getting-started/walkthroughs/datastream_api.html).
-
-If you are writing a batch processing application and you are looking for inspiration what to write,
-take a look at the [Batch Application Examples]({{ site.baseurl }}/dev/batch/examples.html).
-
-For a complete overview over the APIs, have a look at the
-[DataStream API]({{ site.baseurl }}/dev/datastream_api.html) and
-[DataSet API]({{ site.baseurl }}/dev/batch/index.html) sections.
-
-[Here]({{ site.baseurl }}/ops/deployment/local.html) you can find out how to run an application outside the IDE on a local cluster.
-
-If you have any trouble, ask on our
-[Mailing List](http://mail-archives.apache.org/mod_mbox/flink-user/).
-We are happy to provide help.
-
-{% top %}
diff --git a/docs/getting-started/project-setup/java_api_quickstart.zh.md b/docs/getting-started/project-setup/java_api_quickstart.zh.md
deleted file mode 100644
index a5a0493a983781..00000000000000
--- a/docs/getting-started/project-setup/java_api_quickstart.zh.md
+++ /dev/null
@@ -1,360 +0,0 @@
----
-title: "Java 项目模板"
-nav-title: Java 项目模板
-nav-parent_id: project-setup
-nav-pos: 0
----
-
-
-* This will be replaced by the TOC
-{:toc}
-
-
-## 构建工具
-
-Flink项目可以使用不同的构建工具进行构建。
-为了能够快速入门,Flink 为以下构建工具提供了项目模版:
-
-- [Maven](#maven)
-- [Gradle](#gradle)
-
-这些模版可以帮助你搭建项目结构并创建初始构建文件。
-
-## Maven
-
-### 环境要求
-
-唯一的要求是使用 __Maven 3.0.4__ (或更高版本)和安装 __Java 8.x__。
-
-### 创建项目
-
-使用以下命令之一来 __创建项目__:
-
-
-{% highlight bash %}
-$ sbt new tillrohrmann/flink-project.g8
-{% endhighlight %}
- This will prompt you for a couple of parameters (project name, flink version...) and then create a Flink project from the flink-project template.
- You need sbt >= 0.13.13 to execute this command. You can follow this installation guide to obtain it if necessary.
-
-
-{% highlight bash %}
-$ bash <(curl https://flink.apache.org/q/sbt-quickstart.sh)
-{% endhighlight %}
- This will create a Flink project in the specified project directory.
-
-
-
-### Build Project
-
-In order to build your project you simply have to issue the `sbt clean assembly` command.
-This will create the fat-jar __your-project-name-assembly-0.1-SNAPSHOT.jar__ in the directory __target/scala_your-major-scala-version/__.
-
-### Run Project
-
-In order to run your project you have to issue the `sbt run` command.
-
-Per default, this will run your job in the same JVM as `sbt` is running.
-In order to run your job in a distinct JVM, add the following line to `build.sbt`
-
-{% highlight scala %}
-fork in run := true
-{% endhighlight %}
-
-
-#### IntelliJ
-
-We recommend using [IntelliJ](https://www.jetbrains.com/idea/) for your Flink job development.
-In order to get started, you have to import your newly created project into IntelliJ.
-You can do this via `File -> New -> Project from Existing Sources...` and then choosing your project's directory.
-IntelliJ will then automatically detect the `build.sbt` file and set everything up.
-
-In order to run your Flink job, it is recommended to choose the `mainRunner` module as the classpath of your __Run/Debug Configuration__.
-This will ensure, that all dependencies which are set to _provided_ will be available upon execution.
-You can configure the __Run/Debug Configurations__ via `Run -> Edit Configurations...` and then choose `mainRunner` from the _Use classpath of module_ dropbox.
-
-#### Eclipse
-
-In order to import the newly created project into [Eclipse](https://eclipse.org/), you first have to create Eclipse project files for it.
-These project files can be created via the [sbteclipse](https://github.com/typesafehub/sbteclipse) plugin.
-Add the following line to your `PROJECT_DIR/project/plugins.sbt` file:
-
-{% highlight bash %}
-addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "4.0.0")
-{% endhighlight %}
-
-In `sbt` use the following command to create the Eclipse project files
-
-{% highlight bash %}
-> eclipse
-{% endhighlight %}
-
-Now you can import the project into Eclipse via `File -> Import... -> Existing Projects into Workspace` and then select the project directory.
-
-## Maven
-
-### Requirements
-
-The only requirements are working __Maven 3.0.4__ (or higher) and __Java 8.x__ installations.
-
-
-### Create Project
-
-Use one of the following commands to __create a project__:
-
-
-{% highlight bash %}
-$ mvn archetype:generate \
- -DarchetypeGroupId=org.apache.flink \
- -DarchetypeArtifactId=flink-quickstart-scala \{% unless site.is_stable %}
- -DarchetypeCatalog=https://repository.apache.org/content/repositories/snapshots/ \{% endunless %}
- -DarchetypeVersion={{site.version}}
-{% endhighlight %}
- This allows you to name your newly created project. It will interactively ask you for the groupId, artifactId, and package name.
-
- Note: For Maven 3.0 or higher, it is no longer possible to specify the repository (-DarchetypeCatalog) via the command line. For details about this change, please refer to Maven official document
- If you wish to use the snapshot repository, you need to add a repository entry to your settings.xml. For example:
-{% highlight bash %}
-
-
- apache
-
-
-
- apache
-
-
- apache-snapshots
- https://repository.apache.org/content/repositories/snapshots/
-
-
-
-
-
-{% endhighlight %}
-
- {% endunless %}
-
-
-
-### Inspect Project
-
-There will be a new directory in your working directory. If you've used
-the _curl_ approach, the directory is called `quickstart`. Otherwise,
-it has the name of your `artifactId`:
-
-{% highlight bash %}
-$ tree quickstart/
-quickstart/
-├── pom.xml
-└── src
- └── main
- ├── resources
- │ └── log4j2.properties
- └── scala
- └── org
- └── myorg
- └── quickstart
- ├── BatchJob.scala
- └── StreamingJob.scala
-{% endhighlight %}
-
-The sample project is a __Maven project__, which contains two classes: _StreamingJob_ and _BatchJob_ are the basic skeleton programs for a *DataStream* and *DataSet* program.
-The _main_ method is the entry point of the program, both for in-IDE testing/execution and for proper deployments.
-
-We recommend you __import this project into your IDE__.
-
-IntelliJ IDEA supports Maven out of the box and offers a plugin for Scala development.
-From our experience, IntelliJ provides the best experience for developing Flink applications.
-
-For Eclipse, you need the following plugins, which you can install from the provided Eclipse Update Sites:
-
-* _Eclipse 4.x_
- * [Scala IDE](http://download.scala-ide.org/sdk/lithium/e44/scala211/stable/site)
- * [m2eclipse-scala](http://alchim31.free.fr/m2e-scala/update-site)
- * [Build Helper Maven Plugin](https://repo1.maven.org/maven2/.m2e/connectors/m2eclipse-buildhelper/0.15.0/N/0.15.0.201207090124/)
-* _Eclipse 3.8_
- * [Scala IDE for Scala 2.11](http://download.scala-ide.org/sdk/helium/e38/scala211/stable/site) or [Scala IDE for Scala 2.10](http://download.scala-ide.org/sdk/helium/e38/scala210/stable/site)
- * [m2eclipse-scala](http://alchim31.free.fr/m2e-scala/update-site)
- * [Build Helper Maven Plugin](https://repository.sonatype.org/content/repositories/forge-sites/m2e-extras/0.14.0/N/0.14.0.201109282148/)
-
-### Build Project
-
-If you want to __build/package your project__, go to your project directory and
-run the '`mvn clean package`' command.
-You will __find a JAR file__ that contains your application, plus connectors and libraries
-that you may have added as dependencies to the application: `target/-.jar`.
-
-__Note:__ If you use a different class than *StreamingJob* as the application's main class / entry point,
-we recommend you change the `mainClass` setting in the `pom.xml` file accordingly. That way, the Flink
-can run time application from the JAR file without additionally specifying the main class.
-
-
-## Next Steps
-
-Write your application!
-
-If you are writing a streaming application and you are looking for inspiration what to write,
-take a look at the [Stream Processing Application Tutorial]({{ site.baseurl }}/getting-started/walkthroughs/datastream_api.html)
-
-If you are writing a batch processing application and you are looking for inspiration what to write,
-take a look at the [Batch Application Examples]({{ site.baseurl }}/dev/batch/examples.html)
-
-For a complete overview over the APIs, have a look at the
-[DataStream API]({{ site.baseurl }}/dev/datastream_api.html) and
-[DataSet API]({{ site.baseurl }}/dev/batch/index.html) sections.
-
-[Here]({{ site.baseurl }}/ops/deployment/local.html) you can find out how to run an application outside the IDE on a local cluster.
-
-If you have any trouble, ask on our
-[Mailing List](http://mail-archives.apache.org/mod_mbox/flink-user/).
-We are happy to provide help.
-
-{% top %}
diff --git a/docs/getting-started/project-setup/scala_api_quickstart.zh.md b/docs/getting-started/project-setup/scala_api_quickstart.zh.md
deleted file mode 100644
index 8628d078a221ce..00000000000000
--- a/docs/getting-started/project-setup/scala_api_quickstart.zh.md
+++ /dev/null
@@ -1,241 +0,0 @@
----
-title: "Scala 项目模板"
-nav-title: Scala 项目模板
-nav-parent_id: project-setup
-nav-pos: 1
----
-
-
-* This will be replaced by the TOC
-{:toc}
-
-
-## 构建工具
-
-可以使用不同的构建工具来构建Flink项目。
-为了快速入门,Flink为以下构建工具提供了项目模板:
-
-- [SBT](#sbt)
-- [Maven](#maven)
-
-这些模板将帮助你建立项目的框架并创建初始化的构建文件。
-
-## SBT
-
-### 创建项目
-
-你可以通过以下两种方法之一构建新项目:
-
-
-
-
-### 检查项目
-
-项目创建后,工作目录中将多出一个新目录。如果你使用的是 _curl_ 方式创建项目,目录称为 `quickstart`,如果是另外一种创建方式,目录则称为你指定的 `artifactId`。
-
-{% highlight bash %}
-$ tree quickstart/
-quickstart/
-├── pom.xml
-└── src
- └── main
- ├── resources
- │ └── log4j2.properties
- └── scala
- └── org
- └── myorg
- └── quickstart
- ├── BatchJob.scala
- └── StreamingJob.scala
-{% endhighlight %}
-
-样例项目是一个 __Maven 项目__, 包含了两个类: _StreamingJob_ 和 _BatchJob_ 是 *DataStream* 和 *DataSet* 程序的基本框架程序.
-_main_ 方法是程序的入口, 既用于 IDE 内的测试/执行,也用于合理部署。
-
-我们建议你将 __此项目导入你的 IDE__。
-
-IntelliJ IDEA 支持 Maven 开箱即用,并为Scala开发提供插件。
-从我们的经验来看,IntelliJ 提供了最好的Flink应用程序开发体验。
-
-对于 Eclipse,需要以下的插件,你可以从提供的 Eclipse Update Sites 安装这些插件:
-
-* _Eclipse 4.x_
- * [Scala IDE](http://download.scala-ide.org/sdk/lithium/e44/scala211/stable/site)
- * [m2eclipse-scala](http://alchim31.free.fr/m2e-scala/update-site)
- * [Build Helper Maven Plugin](https://repo1.maven.org/maven2/.m2e/connectors/m2eclipse-buildhelper/0.15.0/N/0.15.0.201207090124/)
-* _Eclipse 3.8_
- * [Scala IDE for Scala 2.11](http://download.scala-ide.org/sdk/helium/e38/scala211/stable/site) 或者 [Scala IDE for Scala 2.10](http://download.scala-ide.org/sdk/helium/e38/scala210/stable/site)
- * [m2eclipse-scala](http://alchim31.free.fr/m2e-scala/update-site)
- * [Build Helper Maven Plugin](https://repository.sonatype.org/content/repositories/forge-sites/m2e-extras/0.14.0/N/0.14.0.201109282148/)
-
-### 构建
-
-如果你想要 __构建/打包你的项目__, 进入到你的项目目录,并执行命令‘`mvn clean package`’。
-你将 __找到一个 JAR 文件__,其中包含了你的应用程序,以及已作为依赖项添加到应用程序的连接器和库:`target/-.jar`。
-
-__注意:__ 如果你使用其他类而不是 *StreamingJob* 作为应用程序的主类/入口,我们建议你相应地更改 `pom.xml` 文件中 `mainClass` 的设置。这样,Flink 运行应用程序时无需另外指定主类。
-
-
-## 下一步
-
-开始编写你的应用!
-
-如果你准备编写流处理应用,正在寻找灵感来写什么,
-可以看看[流处理应用程序教程]({{ site.baseurl }}/zh/getting-started/walkthroughs/datastream_api.html)
-
-如果你准备编写批处理应用,正在寻找灵感来写什么,
-可以看看[批处理应用程序示例]({{ site.baseurl }}/zh/dev/batch/examples.html)
-
-有关 API 的完整概述,请查看
-[DataStream API]({{ site.baseurl }}/zh/dev/datastream_api.html) 和
-[DataSet API]({{ site.baseurl }}/zh/dev/batch/index.html) 部分。
-
-在[这里]({{ site.baseurl }}/zh/ops/deployment/local.html),你可以找到如何在IDE外的本地集群中运行应用程序。
-
-如果你有任何问题,请发信至我们的[邮箱列表](http://mail-archives.apache.org/mod_mbox/flink-user/)。
-我们很乐意提供帮助。
-
-{% top %}
diff --git a/docs/getting-started/walkthroughs/datastream_api.md b/docs/getting-started/walkthroughs/datastream_api.md
deleted file mode 100644
index 7281a65767b2e4..00000000000000
--- a/docs/getting-started/walkthroughs/datastream_api.md
+++ /dev/null
@@ -1,944 +0,0 @@
----
-title: "DataStream API"
-nav-id: datastreamwalkthrough
-nav-title: 'DataStream API'
-nav-parent_id: walkthroughs
-nav-pos: 1
----
-
-
-Apache Flink offers a DataStream API for building robust, stateful streaming applications.
-It provides fine-grained control over state and time, which allows for the implementation of advanced event-driven systems.
-In this step-by-step guide you'll learn how to build a stateful streaming application with Flink's DataStream API.
-
-* This will be replaced by the TOC
-{:toc}
-
-## What Are You Building?
-
-Credit card fraud is a growing concern in the digital age.
-Criminals steal credit card numbers by running scams or hacking into insecure systems.
-Stolen numbers are tested by making one or more small purchases, often for a dollar or less.
-If that works, they then make more significant purchases to get items they can sell or keep for themselves.
-
-In this tutorial, you will build a fraud detection system for alerting on suspicious credit card transactions.
-Using a simple set of rules, you will see how Flink allows us to implement advanced business logic and act in real-time.
-
-## Prerequisites
-
-This walkthrough assumes that you have some familiarity with Java or Scala, but you should be able to follow along even if you are coming from a different programming language.
-
-## Help, I’m Stuck!
-
-If you get stuck, check out the [community support resources](https://flink.apache.org/gettinghelp.html).
-In particular, Apache Flink's [user mailing list](https://flink.apache.org/community.html#mailing-lists) is consistently ranked as one of the most active of any Apache project and a great way to get help quickly.
-
-## How to Follow Along
-
-If you want to follow along, you will require a computer with:
-
-* Java 8 or 11
-* Maven
-
-A provided Flink Maven Archetype will create a skeleton project with all the necessary dependencies quickly, so you only need to focus on filling out the business logic.
-These dependencies include `flink-streaming-java` which is the core dependency for all Flink streaming applications and `flink-walkthrough-common` that has data generators and other classes specific to this walkthrough.
-
-{% panel **Note:** Each code block within this walkthrough may not contain the full surrounding class for brevity. The full code is available [at the bottom of the page](#final-application). %}
-
-
- Note: For Maven 3.0 or higher, it is no longer possible to specify the repository (-DarchetypeCatalog) via the command line. For details about this change, please refer to Maven official document
- If you wish to use the snapshot repository, you need to add a repository entry to your settings.xml. For example:
-{% highlight bash %}
-
-
- apache
-
-
-
- apache
-
-
- apache-snapshots
- https://repository.apache.org/content/repositories/snapshots/
-
-
-
-
-
-{% endhighlight %}
-
-{% endunless %}
-
-You can edit the `groupId`, `artifactId` and `package` if you like. With the above parameters,
-Maven will create a folder named `frauddetection` that contains a project with all the dependencies to complete this tutorial.
-After importing the project into your editor, you can find a file `FraudDetectionJob.java` (or `FraudDetectionJob.scala`) with the following code which you can run directly inside your IDE.
-Try setting break points through out the data stream and run the code in DEBUG mode to get a feeling for how everything works.
-
-
-
-## Breaking Down the Code
-
-Let's walk step-by-step through the code of these two files. The `FraudDetectionJob` class defines the data flow of the application and the `FraudDetector` class defines the business logic of the function that detects fraudulent transactions.
-
-We start describing how the Job is assembled in the `main` method of the `FraudDetectionJob` class.
-
-#### The Execution Environment
-
-The first line sets up your `StreamExecutionEnvironment`.
-The execution environment is how you set properties for your Job, create your sources, and finally trigger the execution of the Job.
-
-
-
-#### Creating a Source
-
-Sources ingest data from external systems, such as Apache Kafka, Rabbit MQ, or Apache Pulsar, into Flink Jobs.
-This walkthrough uses a source that generates an infinite stream of credit card transactions for you to process.
-Each transaction contains an account ID (`accountId`), timestamp (`timestamp`) of when the transaction occurred, and US$ amount (`amount`).
-The `name` attached to the source is just for debugging purposes, so if something goes wrong, we will know where the error originated.
-
-
-
-
-#### Partitioning Events & Detecting Fraud
-
-The `transactions` stream contains a lot of transactions from a large number of users, such that it needs to be processed in parallel by multiple fraud detection tasks. Since fraud occurs on a per-account basis, you must ensure that all transactions for the same account are processed by the same parallel task of the fraud detector operator.
-
-To ensure that the same physical task processes all records for a particular key, you can partition a stream using `DataStream#keyBy`.
-The `process()` call adds an operator that applies a function to each partitioned element in the stream.
-It is common to say the operator immediately after a `keyBy`, in this case `FraudDetector`, is executed within a _keyed context_.
-
-
-
-
-#### Outputting Results
-
-A sink writes a `DataStream` to an external system; such as Apache Kafka, Cassandra, and AWS Kinesis.
-The `AlertSink` logs each `Alert` record with log level **INFO**, instead of writing it to persistent storage, so you can easily see your results.
-
-
-
-#### Executing the Job
-
-Flink applications are built lazily and shipped to the cluster for execution only once fully formed.
-Call `StreamExecutionEnvironment#execute` to begin the execution of our Job and give it a name.
-
-
-
-#### The Fraud Detector
-
-The fraud detector is implemented as a `KeyedProcessFunction`.
-Its method `KeyedProcessFunction#processElement` is called for every transaction event.
-This first version produces an alert on every transaction, which some may say is overly conservative.
-
-The next steps of this tutorial will guide you to expand the fraud detector with more meaningful business logic.
-
-
-
-## Writing a Real Application (v1)
-
-For the first version, the fraud detector should output an alert for any account that makes a small transaction immediately followed by a large one. Where small is anything less than $1.00 and large is more than $500.
-Imagine your fraud detector processes the following stream of transactions for a particular account.
-
-
-
-
-
-Transactions 3 and 4 should be marked as fraudulent because it is a small transaction, $0.09, followed by a large one, $510.
-Alternatively, transactions 7, 8, and 9 are not fraud because the small amount of $0.02 is not immediately followed by the large one; instead, there is an intermediate transaction that breaks the pattern.
-
-To do this, the fraud detector must _remember_ information across events; a large transaction is only fraudulent if the previous one was small.
-Remembering information across events requires [state]({{ site.baseurl }}/concepts/glossary.html#managed-state), and that is why we decided to use a [KeyedProcessFunction]({{ site.baseurl }}/dev/stream/operators/process_function.html).
-It provides fine-grained control over both state and time, which will allow us to evolve our algorithm with more complex requirements throughout this walkthrough.
-
-The most straightforward implementation is a boolean flag that is set whenever a small transaction is processed.
-When a large transaction comes through, you can simply check if the flag is set for that account.
-
-However, merely implementing the flag as a member variable in the `FraudDetector` class will not work.
-Flink processes the transactions of multiple accounts with the same object instance of `FraudDetector`, which means if accounts A and B are routed through the same instance of `FraudDetector`, a transaction for account A could set the flag to true and then a transaction for account B could set off a false alert.
-We could of course use a data structure like a `Map` to keep track of the flags for individual keys, however, a simple member variable would not be fault-tolerant and all its information be lost in case of a failure.
-Hence, the fraud detector would possibly miss alerts if the application ever had to restart to recover from a failure.
-
-To address these challenges, Flink provides primitives for fault-tolerant state that are almost as easy to use as regular member variables.
-
-The most basic type of state in Flink is [ValueState]({{ site.baseurl }}/dev/stream/state/state.html#using-managed-keyed-state), a data type that adds fault tolerance to any variable it wraps.
-`ValueState` is a form of _keyed state_, meaning it is only available in operators that are applied in a _keyed context_; any operator immediately following `DataStream#keyBy`.
-A _keyed state_ of an operator is automatically scoped to the key of the record that is currently processed.
-In this example, the key is the account id for the current transaction (as declared by `keyBy()`), and `FraudDetector` maintains an independent state for each account.
-`ValueState` is created using a `ValueStateDescriptor` which contains metadata about how Flink should manage the variable. The state should be registered before the function starts processing data.
-The right hook for this is the `open()` method.
-
-
-
-`ValueState` is a wrapper class, similar to `AtomicReference` or `AtomicLong` in the Java standard library.
-It provides three methods for interacting with its contents; `update` sets the state, `value` gets the current value, and `clear` deletes its contents.
-If the state for a particular key is empty, such as at the beginning of an application or after calling `ValueState#clear`, then `ValueState#value` will return `null`.
-Modifications to the object returned by `ValueState#value` are not guaranteed to be recognized by the system, and so all changes must be performed with `ValueState#update`.
-Otherwise, fault tolerance is managed automatically by Flink under the hood, and so you can interact with it like with any standard variable.
-
-Below, you can see an example of how you can use a flag state to track potential fraudulent transactions.
-
-
-
-{% highlight java %}
-@Override
-public void processElement(
- Transaction transaction,
- Context context,
- Collector collector) throws Exception {
-
- // Get the current state for the current key
- Boolean lastTransactionWasSmall = flagState.value();
-
- // Check if the flag is set
- if (lastTransactionWasSmall != null) {
- if (transaction.getAmount() > LARGE_AMOUNT) {
- // Output an alert downstream
- Alert alert = new Alert();
- alert.setId(transaction.getAccountId());
-
- collector.collect(alert);
- }
-
- // Clean up our state
- flagState.clear();
- }
-
- if (transaction.getAmount() < SMALL_AMOUNT) {
- // Set the flag to true
- flagState.update(true);
- }
-}
-{% endhighlight %}
-
-
-
-{% highlight scala %}
-override def processElement(
- transaction: Transaction,
- context: KeyedProcessFunction[Long, Transaction, Alert]#Context,
- collector: Collector[Alert]): Unit = {
-
- // Get the current state for the current key
- val lastTransactionWasSmall = flagState.value
-
- // Check if the flag is set
- if (lastTransactionWasSmall != null) {
- if (transaction.getAmount > FraudDetector.LARGE_AMOUNT) {
- // Output an alert downstream
- val alert = new Alert
- alert.setId(transaction.getAccountId)
-
- collector.collect(alert)
- }
- // Clean up our state
- flagState.clear()
- }
-
- if (transaction.getAmount < FraudDetector.SMALL_AMOUNT) {
- // set the flag to true
- flagState.update(true)
- }
-}
-{% endhighlight %}
-
-
-
-For every transaction, the fraud detector checks the state of the flag for that account.
-Remember, `ValueState` is always scoped to the current key, i.e., account.
-If the flag is non-null, then the last transaction seen for that account was small, and so if the amount for this transaction is large, then the detector outputs a fraud alert.
-
-After that check, the flag state is unconditionally cleared.
-Either the current transaction caused a fraud alert, and the pattern is over, or the current transaction did not cause an alert, and the pattern is broken and needs to be restarted.
-
-Finally, the transaction amount is checked to see if it is small.
-If so, then the flag is set so that it can be checked by the next event.
-Notice that `ValueState` actually has three states, unset ( `null`), `true`, and `false`, because all `ValueState`'s are nullable.
-This job only makes use of unset ( `null`) and `true` to check whether the flag is set or not.
-
-## Fraud Detector v2: State + Time = ❤️
-
-Scammers don't wait long to make their large purchase to reduce the chances their test transaction is noticed.
-For example, suppose you wanted to set a 1 minute timeout to your fraud detector; i.e., in the previous example transactions 3 and 4 would only be considered fraud if they occurred within 1 minute of each other.
-Flink's `KeyedProcessFunction` allows you to set timers which invoke a callback method at some point in time in the future.
-
-Let's see how we can modify our Job to comply with our new requirements:
-
-* Whenever the flag is set to `true`, also set a timer for 1 minute in the future.
-* When the timer fires, reset the flag by clearing its state.
-* If the flag is ever cleared the timer should be canceled.
-
-To cancel a timer, you have to remember what time it is set for, and remembering implies state, so you will begin by creating a timer state along with your flag state.
-
-
-{% highlight scala %}
-@SerialVersionUID(1L)
-class FraudDetector extends KeyedProcessFunction[Long, Transaction, Alert] {
-
- @transient private var flagState: ValueState[java.lang.Boolean] = _
- @transient private var timerState: ValueState[java.lang.Long] = _
-
- @throws[Exception]
- override def open(parameters: Configuration): Unit = {
- val flagDescriptor = new ValueStateDescriptor("flag", Types.BOOLEAN)
- flagState = getRuntimeContext.getState(flagDescriptor)
-
- val timerDescriptor = new ValueStateDescriptor("timer-state", Types.LONG)
- timerState = getRuntimeContext.getState(timerDescriptor)
- }
-{% endhighlight %}
-
-
-
-`KeyedProcessFunction#processElement` is called with a `Context` that contains a timer service.
-The timer service can be used to query the current time, register timers, and delete timers.
-With this, you can set a timer for 1 minute in the future every time the flag is set and store the timestamp in `timerState`.
-
-
-
-{% highlight java %}
-if (transaction.getAmount() < SMALL_AMOUNT) {
- // set the flag to true
- flagState.update(true);
-
- // set the timer and timer state
- long timer = context.timerService().currentProcessingTime() + ONE_MINUTE;
- context.timerService().registerProcessingTimeTimer(timer);
- timerState.update(timer);
-}
-{% endhighlight %}
-
-
-{% highlight scala %}
-if (transaction.getAmount < FraudDetector.SMALL_AMOUNT) {
- // set the flag to true
- flagState.update(true)
-
- // set the timer and timer state
- val timer = context.timerService.currentProcessingTime + FraudDetector.ONE_MINUTE
- context.timerService.registerProcessingTimeTimer(timer)
- timerState.update(timer)
-}
-{% endhighlight %}
-
-
-
-Processing time is wall clock time, and is determined by the system clock of the machine running the operator.
-
-When a timer fires, it calls `KeyedProcessFunction#onTimer`.
-Overriding this method is how you can implement your callback to reset the flag.
-
-
-
-Finally, to cancel the timer, you need to delete the registered timer and delete the timer state.
-You can wrap this in a helper method and call this method instead of `flagState.clear()`.
-
-
-
-{% highlight java %}
-private void cleanUp(Context ctx) throws Exception {
- // delete timer
- Long timer = timerState.value();
- ctx.timerService().deleteProcessingTimeTimer(timer);
-
- // clean up all state
- timerState.clear();
- flagState.clear();
-}
-{% endhighlight %}
-
-
-
-{% highlight scala %}
-@throws[Exception]
-private def cleanUp(ctx: KeyedProcessFunction[Long, Transaction, Alert]#Context): Unit = {
- // delete timer
- val timer = timerState.value
- ctx.timerService.deleteProcessingTimeTimer(timer)
-
- // clean up all states
- timerState.clear()
- flagState.clear()
-}
-{% endhighlight %}
-
-
-
-And that's it, a fully functional, stateful, distributed streaming application!
-
-## Final Application
-
-
-
-{% highlight java %}
-package spendreport;
-
-import org.apache.flink.api.common.state.ValueState;
-import org.apache.flink.api.common.state.ValueStateDescriptor;
-import org.apache.flink.api.common.typeinfo.Types;
-import org.apache.flink.configuration.Configuration;
-import org.apache.flink.streaming.api.functions.KeyedProcessFunction;
-import org.apache.flink.util.Collector;
-import org.apache.flink.walkthrough.common.entity.Alert;
-import org.apache.flink.walkthrough.common.entity.Transaction;
-
-public class FraudDetector extends KeyedProcessFunction {
-
- private static final long serialVersionUID = 1L;
-
- private static final double SMALL_AMOUNT = 1.00;
- private static final double LARGE_AMOUNT = 500.00;
- private static final long ONE_MINUTE = 60 * 1000;
-
- private transient ValueState flagState;
- private transient ValueState timerState;
-
- @Override
- public void open(Configuration parameters) {
- ValueStateDescriptor flagDescriptor = new ValueStateDescriptor<>(
- "flag",
- Types.BOOLEAN);
- flagState = getRuntimeContext().getState(flagDescriptor);
-
- ValueStateDescriptor timerDescriptor = new ValueStateDescriptor<>(
- "timer-state",
- Types.LONG);
- timerState = getRuntimeContext().getState(timerDescriptor);
- }
-
- @Override
- public void processElement(
- Transaction transaction,
- Context context,
- Collector collector) throws Exception {
-
- // Get the current state for the current key
- Boolean lastTransactionWasSmall = flagState.value();
-
- // Check if the flag is set
- if (lastTransactionWasSmall != null) {
- if (transaction.getAmount() > LARGE_AMOUNT) {
- //Output an alert downstream
- Alert alert = new Alert();
- alert.setId(transaction.getAccountId());
-
- collector.collect(alert);
- }
- // Clean up our state
- cleanUp(context);
- }
-
- if (transaction.getAmount() < SMALL_AMOUNT) {
- // set the flag to true
- flagState.update(true);
-
- long timer = context.timerService().currentProcessingTime() + ONE_MINUTE;
- context.timerService().registerProcessingTimeTimer(timer);
-
- timerState.update(timer);
- }
- }
-
- @Override
- public void onTimer(long timestamp, OnTimerContext ctx, Collector out) {
- // remove flag after 1 minute
- timerState.clear();
- flagState.clear();
- }
-
- private void cleanUp(Context ctx) throws Exception {
- // delete timer
- Long timer = timerState.value();
- ctx.timerService().deleteProcessingTimeTimer(timer);
-
- // clean up all state
- timerState.clear();
- flagState.clear();
- }
-}
-{% endhighlight %}
-
-
-
-{% highlight scala %}
-package spendreport
-
-import org.apache.flink.api.common.state.{ValueState, ValueStateDescriptor}
-import org.apache.flink.api.scala.typeutils.Types
-import org.apache.flink.configuration.Configuration
-import org.apache.flink.streaming.api.functions.KeyedProcessFunction
-import org.apache.flink.util.Collector
-import org.apache.flink.walkthrough.common.entity.Alert
-import org.apache.flink.walkthrough.common.entity.Transaction
-
-object FraudDetector {
- val SMALL_AMOUNT: Double = 1.00
- val LARGE_AMOUNT: Double = 500.00
- val ONE_MINUTE: Long = 60 * 1000L
-}
-
-@SerialVersionUID(1L)
-class FraudDetector extends KeyedProcessFunction[Long, Transaction, Alert] {
-
- @transient private var flagState: ValueState[java.lang.Boolean] = _
- @transient private var timerState: ValueState[java.lang.Long] = _
-
- @throws[Exception]
- override def open(parameters: Configuration): Unit = {
- val flagDescriptor = new ValueStateDescriptor("flag", Types.BOOLEAN)
- flagState = getRuntimeContext.getState(flagDescriptor)
-
- val timerDescriptor = new ValueStateDescriptor("timer-state", Types.LONG)
- timerState = getRuntimeContext.getState(timerDescriptor)
- }
-
- override def processElement(
- transaction: Transaction,
- context: KeyedProcessFunction[Long, Transaction, Alert]#Context,
- collector: Collector[Alert]): Unit = {
-
- // Get the current state for the current key
- val lastTransactionWasSmall = flagState.value
-
- // Check if the flag is set
- if (lastTransactionWasSmall != null) {
- if (transaction.getAmount > FraudDetector.LARGE_AMOUNT) {
- // Output an alert downstream
- val alert = new Alert
- alert.setId(transaction.getAccountId)
-
- collector.collect(alert)
- }
- // Clean up our state
- cleanUp(context)
- }
-
- if (transaction.getAmount < FraudDetector.SMALL_AMOUNT) {
- // set the flag to true
- flagState.update(true)
- val timer = context.timerService.currentProcessingTime + FraudDetector.ONE_MINUTE
-
- context.timerService.registerProcessingTimeTimer(timer)
- timerState.update(timer)
- }
- }
-
- override def onTimer(
- timestamp: Long,
- ctx: KeyedProcessFunction[Long, Transaction, Alert]#OnTimerContext,
- out: Collector[Alert]): Unit = {
- // remove flag after 1 minute
- timerState.clear()
- flagState.clear()
- }
-
- @throws[Exception]
- private def cleanUp(ctx: KeyedProcessFunction[Long, Transaction, Alert]#Context): Unit = {
- // delete timer
- val timer = timerState.value
- ctx.timerService.deleteProcessingTimeTimer(timer)
-
- // clean up all states
- timerState.clear()
- flagState.clear()
- }
-}
-{% endhighlight %}
-
-
-
-### Expected Output
-
-Running this code with the provided `TransactionSource` will emit fraud alerts for account 3.
-You should see the following output in your task manager logs:
-
-{% highlight bash %}
-2019-08-19 14:22:06,220 INFO org.apache.flink.walkthrough.common.sink.AlertSink - Alert{id=3}
-2019-08-19 14:22:11,383 INFO org.apache.flink.walkthrough.common.sink.AlertSink - Alert{id=3}
-2019-08-19 14:22:16,551 INFO org.apache.flink.walkthrough.common.sink.AlertSink - Alert{id=3}
-2019-08-19 14:22:21,723 INFO org.apache.flink.walkthrough.common.sink.AlertSink - Alert{id=3}
-2019-08-19 14:22:26,896 INFO org.apache.flink.walkthrough.common.sink.AlertSink - Alert{id=3}
-{% endhighlight %}
diff --git a/docs/getting-started/walkthroughs/datastream_api.zh.md b/docs/getting-started/walkthroughs/datastream_api.zh.md
deleted file mode 100644
index f21004d495a01a..00000000000000
--- a/docs/getting-started/walkthroughs/datastream_api.zh.md
+++ /dev/null
@@ -1,946 +0,0 @@
----
-title: "DataStream API"
-nav-id: datastreamwalkthrough
-nav-title: 'DataStream API'
-nav-parent_id: walkthroughs
-nav-pos: 1
----
-
-
-Apache Flink 提供了 DataStream API 来实现稳定可靠的、有状态的流处理应用程序。
-Flink 支持对状态和时间的细粒度控制,以此来实现复杂的事件驱动数据处理系统。
-这个入门指导手册讲述了如何通过 Flink DataStream API 来实现一个有状态流处理程序。
-
-* This will be replaced by the TOC
-{:toc}
-
-## 你要搭建一个什么系统
-
-在当今数字时代,信用卡欺诈行为越来越被重视。
-罪犯可以通过诈骗或者入侵安全级别较低系统来盗窃信用卡卡号。
-用盗得的信用卡进行很小额度的例如一美元或者更小额度的消费进行测试。
-如果测试消费成功,那么他们就会用这个信用卡进行大笔消费,来购买一些他们希望得到的,或者可以倒卖的财物。
-
-在这个教程中,你将会建立一个针对可疑信用卡交易行为的反欺诈检测系统。
-通过使用一组简单的规则,你将了解到 Flink 如何为我们实现复杂业务逻辑并实时执行。
-
-## 准备条件
-
-这个代码练习假定你对 Java 或 Scala 有一定的了解,当然,如果你之前使用的是其他开发语言,你也应该能够跟随本教程进行学习。
-
-## 困难求助
-
-如果遇到困难,可以参考 [社区支持资源](https://flink.apache.org/zh/gettinghelp.html)。
-当然也可以在邮件列表提问,Flink 的 [用户邮件列表](https://flink.apache.org/zh/community.html#mailing-lists) 一直被评为所有Apache项目中最活跃的一个,这也是快速获得帮助的好方法。
-
-## 怎样跟着教程练习
-
-首先,你需要在你的电脑上准备以下环境:
-
-* Java 8 or 11
-* Maven
-
-一个准备好的 Flink Maven Archetype 能够快速创建一个包含了必要依赖的 Flink 程序骨架,基于此,你可以把精力集中在编写业务逻辑上即可。
-这些已包含的依赖包括 `flink-streaming-java`、`flink-walkthrough-common` 等,他们分别是 Flink 应用程序的核心依赖项和这个代码练习需要的数据生成器,当然还包括其他本代码练习所依赖的类。
-
-{% panel **说明:** 为简洁起见,本练习中的代码块中可能不包含完整的类路径。完整的类路径可以在文档底部 [链接](#完整的程序) 中找到。 %}
-
-
-{% highlight java %}
-@Override
-public void processElement(
- Transaction transaction,
- Context context,
- Collector collector) throws Exception {
-
- // Get the current state for the current key
- Boolean lastTransactionWasSmall = flagState.value();
-
- // Check if the flag is set
- if (lastTransactionWasSmall != null) {
- if (transaction.getAmount() > LARGE_AMOUNT) {
- // Output an alert downstream
- Alert alert = new Alert();
- alert.setId(transaction.getAccountId());
-
- collector.collect(alert);
- }
-
- // Clean up our state
- flagState.clear();
- }
-
- if (transaction.getAmount() < SMALL_AMOUNT) {
- // Set the flag to true
- flagState.update(true);
- }
-}
-{% endhighlight %}
-
-
-
-{% highlight scala %}
-override def processElement(
- transaction: Transaction,
- context: KeyedProcessFunction[Long, Transaction, Alert]#Context,
- collector: Collector[Alert]): Unit = {
-
- // Get the current state for the current key
- val lastTransactionWasSmall = flagState.value
-
- // Check if the flag is set
- if (lastTransactionWasSmall != null) {
- if (transaction.getAmount > FraudDetector.LARGE_AMOUNT) {
- // Output an alert downstream
- val alert = new Alert
- alert.setId(transaction.getAccountId)
-
- collector.collect(alert)
- }
- // Clean up our state
- flagState.clear()
- }
-
- if (transaction.getAmount < FraudDetector.SMALL_AMOUNT) {
- // set the flag to true
- flagState.update(true)
- }
-}
-{% endhighlight %}
-
-{% highlight java %}
-if (transaction.getAmount() < SMALL_AMOUNT) {
- // set the flag to true
- flagState.update(true);
-
- // set the timer and timer state
- long timer = context.timerService().currentProcessingTime() + ONE_MINUTE;
- context.timerService().registerProcessingTimeTimer(timer);
- timerState.update(timer);
-}
-{% endhighlight %}
-
-
-{% highlight scala %}
-if (transaction.getAmount < FraudDetector.SMALL_AMOUNT) {
- // set the flag to true
- flagState.update(true)
-
- // set the timer and timer state
- val timer = context.timerService.currentProcessingTime + FraudDetector.ONE_MINUTE
- context.timerService.registerProcessingTimeTimer(timer)
- timerState.update(timer)
-}
-{% endhighlight %}
-
-{% highlight java %}
-private void cleanUp(Context ctx) throws Exception {
- // delete timer
- Long timer = timerState.value();
- ctx.timerService().deleteProcessingTimeTimer(timer);
-
- // clean up all state
- timerState.clear();
- flagState.clear();
-}
-{% endhighlight %}
-
-
-
-{% highlight scala %}
-@throws[Exception]
-private def cleanUp(ctx: KeyedProcessFunction[Long, Transaction, Alert]#Context): Unit = {
- // delete timer
- val timer = timerState.value
- ctx.timerService.deleteProcessingTimeTimer(timer)
-
- // clean up all states
- timerState.clear()
- flagState.clear()
-}
-{% endhighlight %}
-
-
-
-这就是一个功能完备的,有状态的分布式流处理程序了。
-
-## 完整的程序
-
-
-
-{% highlight java %}
-package spendreport;
-
-import org.apache.flink.api.common.state.ValueState;
-import org.apache.flink.api.common.state.ValueStateDescriptor;
-import org.apache.flink.api.common.typeinfo.Types;
-import org.apache.flink.configuration.Configuration;
-import org.apache.flink.streaming.api.functions.KeyedProcessFunction;
-import org.apache.flink.util.Collector;
-import org.apache.flink.walkthrough.common.entity.Alert;
-import org.apache.flink.walkthrough.common.entity.Transaction;
-
-public class FraudDetector extends KeyedProcessFunction {
-
- private static final long serialVersionUID = 1L;
-
- private static final double SMALL_AMOUNT = 1.00;
- private static final double LARGE_AMOUNT = 500.00;
- private static final long ONE_MINUTE = 60 * 1000;
-
- private transient ValueState flagState;
- private transient ValueState timerState;
-
- @Override
- public void open(Configuration parameters) {
- ValueStateDescriptor flagDescriptor = new ValueStateDescriptor<>(
- "flag",
- Types.BOOLEAN);
- flagState = getRuntimeContext().getState(flagDescriptor);
-
- ValueStateDescriptor timerDescriptor = new ValueStateDescriptor<>(
- "timer-state",
- Types.LONG);
- timerState = getRuntimeContext().getState(timerDescriptor);
- }
-
- @Override
- public void processElement(
- Transaction transaction,
- Context context,
- Collector collector) throws Exception {
-
- // Get the current state for the current key
- Boolean lastTransactionWasSmall = flagState.value();
-
- // Check if the flag is set
- if (lastTransactionWasSmall != null) {
- if (transaction.getAmount() > LARGE_AMOUNT) {
- //Output an alert downstream
- Alert alert = new Alert();
- alert.setId(transaction.getAccountId());
-
- collector.collect(alert);
- }
- // Clean up our state
- cleanUp(context);
- }
-
- if (transaction.getAmount() < SMALL_AMOUNT) {
- // set the flag to true
- flagState.update(true);
-
- long timer = context.timerService().currentProcessingTime() + ONE_MINUTE;
- context.timerService().registerProcessingTimeTimer(timer);
-
- timerState.update(timer);
- }
- }
-
- @Override
- public void onTimer(long timestamp, OnTimerContext ctx, Collector out) {
- // remove flag after 1 minute
- timerState.clear();
- flagState.clear();
- }
-
- private void cleanUp(Context ctx) throws Exception {
- // delete timer
- Long timer = timerState.value();
- ctx.timerService().deleteProcessingTimeTimer(timer);
-
- // clean up all state
- timerState.clear();
- flagState.clear();
- }
-}
-{% endhighlight %}
-
-
-
-{% highlight scala %}
-package spendreport
-
-import org.apache.flink.api.common.state.{ValueState, ValueStateDescriptor}
-import org.apache.flink.api.scala.typeutils.Types
-import org.apache.flink.configuration.Configuration
-import org.apache.flink.streaming.api.functions.KeyedProcessFunction
-import org.apache.flink.util.Collector
-import org.apache.flink.walkthrough.common.entity.Alert
-import org.apache.flink.walkthrough.common.entity.Transaction
-
-object FraudDetector {
- val SMALL_AMOUNT: Double = 1.00
- val LARGE_AMOUNT: Double = 500.00
- val ONE_MINUTE: Long = 60 * 1000L
-}
-
-@SerialVersionUID(1L)
-class FraudDetector extends KeyedProcessFunction[Long, Transaction, Alert] {
-
- @transient private var flagState: ValueState[java.lang.Boolean] = _
- @transient private var timerState: ValueState[java.lang.Long] = _
-
- @throws[Exception]
- override def open(parameters: Configuration): Unit = {
- val flagDescriptor = new ValueStateDescriptor("flag", Types.BOOLEAN)
- flagState = getRuntimeContext.getState(flagDescriptor)
-
- val timerDescriptor = new ValueStateDescriptor("timer-state", Types.LONG)
- timerState = getRuntimeContext.getState(timerDescriptor)
- }
-
- override def processElement(
- transaction: Transaction,
- context: KeyedProcessFunction[Long, Transaction, Alert]#Context,
- collector: Collector[Alert]): Unit = {
-
- // Get the current state for the current key
- val lastTransactionWasSmall = flagState.value
-
- // Check if the flag is set
- if (lastTransactionWasSmall != null) {
- if (transaction.getAmount > FraudDetector.LARGE_AMOUNT) {
- // Output an alert downstream
- val alert = new Alert
- alert.setId(transaction.getAccountId)
-
- collector.collect(alert)
- }
- // Clean up our state
- cleanUp(context)
- }
-
- if (transaction.getAmount < FraudDetector.SMALL_AMOUNT) {
- // set the flag to true
- flagState.update(true)
- val timer = context.timerService.currentProcessingTime + FraudDetector.ONE_MINUTE
-
- context.timerService.registerProcessingTimeTimer(timer)
- timerState.update(timer)
- }
- }
-
- override def onTimer(
- timestamp: Long,
- ctx: KeyedProcessFunction[Long, Transaction, Alert]#OnTimerContext,
- out: Collector[Alert]): Unit = {
- // remove flag after 1 minute
- timerState.clear()
- flagState.clear()
- }
-
- @throws[Exception]
- private def cleanUp(ctx: KeyedProcessFunction[Long, Transaction, Alert]#Context): Unit = {
- // delete timer
- val timer = timerState.value
- ctx.timerService.deleteProcessingTimeTimer(timer)
-
- // clean up all states
- timerState.clear()
- flagState.clear()
- }
-}
-{% endhighlight %}
-
-
-
-### 期望的结果
-
-使用已准备好的 `TransactionSource` 数据源运行这个代码,将会检测到账户 3 的欺诈行为,并输出报警信息。
-你将能够在你的 task manager 的日志中看到下边输出:
-
-{% highlight bash %}
-2019-08-19 14:22:06,220 INFO org.apache.flink.walkthrough.common.sink.AlertSink - Alert{id=3}
-2019-08-19 14:22:11,383 INFO org.apache.flink.walkthrough.common.sink.AlertSink - Alert{id=3}
-2019-08-19 14:22:16,551 INFO org.apache.flink.walkthrough.common.sink.AlertSink - Alert{id=3}
-2019-08-19 14:22:21,723 INFO org.apache.flink.walkthrough.common.sink.AlertSink - Alert{id=3}
-2019-08-19 14:22:26,896 INFO org.apache.flink.walkthrough.common.sink.AlertSink - Alert{id=3}
-{% endhighlight %}
diff --git a/docs/getting-started/walkthroughs/index.md b/docs/getting-started/walkthroughs/index.md
deleted file mode 100644
index 055f1b85050c4e..00000000000000
--- a/docs/getting-started/walkthroughs/index.md
+++ /dev/null
@@ -1,25 +0,0 @@
----
-title: "Code Walkthroughs"
-nav-id: walkthroughs
-nav-title: 'Code Walkthroughs'
-nav-parent_id: getting-started
-nav-pos: 20
----
-
\ No newline at end of file
diff --git a/docs/getting-started/walkthroughs/index.zh.md b/docs/getting-started/walkthroughs/index.zh.md
deleted file mode 100644
index 055f1b85050c4e..00000000000000
--- a/docs/getting-started/walkthroughs/index.zh.md
+++ /dev/null
@@ -1,25 +0,0 @@
----
-title: "Code Walkthroughs"
-nav-id: walkthroughs
-nav-title: 'Code Walkthroughs'
-nav-parent_id: getting-started
-nav-pos: 20
----
-
\ No newline at end of file
diff --git a/docs/getting-started/walkthroughs/table_api.md b/docs/getting-started/walkthroughs/table_api.md
deleted file mode 100644
index 0c99dbca10a1b7..00000000000000
--- a/docs/getting-started/walkthroughs/table_api.md
+++ /dev/null
@@ -1,513 +0,0 @@
----
-title: "Table API"
-nav-id: tableapiwalkthrough
-nav-title: 'Table API'
-nav-parent_id: walkthroughs
-nav-pos: 2
----
-
-
-Apache Flink offers a Table API as a unified, relational API for batch and stream processing, i.e., queries are executed with the same semantics on unbounded, real-time streams or bounded, batch data sets and produce the same results.
-The Table API in Flink is commonly used to ease the definition of data analytics, data pipelining, and ETL applications.
-
-* This will be replaced by the TOC
-{:toc}
-
-## What Will You Be Building?
-
-In this tutorial, you will learn how to build a continuous ETL pipeline for tracking financial transactions by account over time.
-You will start by building your report as a nightly batch job, and then migrate to a streaming pipeline.
-
-## Prerequisites
-
-This walkthrough assumes that you have some familiarity with Java or Scala, but you should be able to follow along even if you are coming from a different programming language.
-It also assumes that you are familiar with basic relational concepts such as `SELECT` and `GROUP BY` clauses.
-
-## Help, I’m Stuck!
-
-If you get stuck, check out the [community support resources](https://flink.apache.org/community.html).
-In particular, Apache Flink's [user mailing list](https://flink.apache.org/community.html#mailing-lists) is consistently ranked as one of the most active of any Apache project and a great way to get help quickly.
-
-## How To Follow Along
-
-If you want to follow along, you will require a computer with:
-
-* Java 8 or 11
-* Maven
-
-A provided Flink Maven Archetype will create a skeleton project with all the necessary dependencies quickly:
-
-
- Note: For Maven 3.0 or higher, it is no longer possible to specify the repository (-DarchetypeCatalog) via the command line. For details about this change, please refer to Maven official document
- If you wish to use the snapshot repository, you need to add a repository entry to your settings.xml. For example:
-{% highlight bash %}
-
-
- apache
-
-
-
- apache
-
-
- apache-snapshots
- https://repository.apache.org/content/repositories/snapshots/
-
-
-
-
-
-{% endhighlight %}
-
-{% endunless %}
-
-You can edit the `groupId`, `artifactId` and `package` if you like. With the above parameters,
-Maven will create a project with all the dependencies to complete this tutorial.
-After importing the project into your editor, you will see a file with the following code which you can run directly inside your IDE.
-
-
-
-## Breaking Down The Code
-
-#### The Execution Environment
-
-The first two lines set up your `ExecutionEnvironment`.
-The execution environment is how you can set properties for your Job, specify whether you are writing a batch or a streaming application, and create your sources.
-This walkthrough begins with the batch environment since you are building a periodic batch report.
-It is then wrapped in a `BatchTableEnvironment` to have full access to the Table API.
-
-
-
-
-#### Registering Tables
-
-Next, tables are registered in the execution environment that you can use to connect to external systems for reading and writing both batch and streaming data.
-A table source provides access to data stored in external systems; such as a database, a key-value store, a message queue, or a file system.
-A table sink emits a table to an external storage system.
-Depending on the type of source and sink, they support different formats such as CSV, JSON, Avro, or Parquet.
-
-
-
-{% highlight java %}
-tEnv.registerTableSource("transactions", new BoundedTransactionTableSource());
-tEnv.registerTableSink("spend_report", new SpendReportTableSink());
-{% endhighlight %}
-
-
-
-{% highlight scala %}
-tEnv.registerTableSource("transactions", new BoundedTransactionTableSource)
-tEnv.registerTableSink("spend_report", new SpendReportTableSink)
-{% endhighlight %}
-
-
-
-Two tables are registered; a transaction input table, and a spend report output table.
-The transactions (`transactions`) table lets us read credit card transactions, which contain account ID's (`accountId`), timestamps (`timestamp`), and US$ amounts (`amount`).
-In this tutorial, the table is backed by data generated in memory to avoid any dependencies on external systems.
-In practice, the `BoundedTransactionTableSource` may be backed by a filesystem, a database, or any other static source.
-The spend report (`spend_report`) table logs each row with log level **INFO**, instead of writing to persistent storage, so you can easily see your results.
-
-#### Registering A UDF
-
-Along with the tables, a [user-defined function]({{ site.baseurl }}/dev/table/functions/udfs.html) is registered for working with timestamps.
-This function takes a timestamp and rounds it down to the nearest hour.
-
-
-
-#### The Query
-
-With the environment configured and tables registered, you are ready to build your first application.
-From the `TableEnvironment` you can `scan` an input table to read its rows and then write those results into an output table using `insertInto`.
-
-
-
-Initially, the Job reads all transactions and logs them out with log level **INFO**.
-
-#### Execute
-
-Flink applications are built lazily and shipped to the cluster for execution only once fully formed.
-You call `ExecutionEnvironment#execute` to begin the execution of your Job by giving it a name.
-
-
-
-## Attempt One
-
-Now with the skeleton of a Job set-up, you are ready to add some business logic.
-The goal is to build a report that shows the total spend for each account across each hour of the day.
-Just like a SQL query, Flink can select the required fields and group by your keys.
-Because the timestamp field has millisecond granularity, you can use the UDF to round it down to the nearest hour.
-Finally, select all the fields, summing the total spend per account-hour pair with the built-in `sum` [aggregate function]({{ site.baseurl }}/dev/table/functions/systemFunctions.html#aggregate-functions).
-
-
-
-This query consumes all records from the `transactions` table, calculates the report, and outputs the results in an efficient, scalable manner.
-
-{% highlight raw %}
-# Query 1 output showing account id, timestamp, and amount
-
-> 1, 2019-01-01 00:00:00.0, $567.87
-> 2, 2019-01-01 00:00:00.0, $726.23
-> 1, 2019-01-01 01:00:00.0, $686.87
-> 2, 2019-01-01 01:00:00.0, $810.06
-> 1, 2019-01-01 02:00:00.0, $859.35
-> 2, 2019-01-01 02:00:00.0, $458.40
-> 1, 2019-01-01 03:00:00.0, $330.85
-> 2, 2019-01-01 03:00:00.0, $730.02
-> 1, 2019-01-01 04:00:00.0, $585.16
-> 2, 2019-01-01 04:00:00.0, $760.76
-{% endhighlight %}
-
-## Adding Windows
-
-Grouping data based on time is a typical operation in data processing, especially when working with infinite streams.
-A grouping based on time is called a [window]({{ site.baseurl }}/dev/stream/operators/windows.html) and Flink offers flexible windowing semantics.
-The most basic type of window is called a `Tumble` window, which has a fixed size and whose buckets do not overlap.
-
-
-{% highlight scala %}
-tEnv
- .scan("transactions")
- .window(Tumble over 1.hour on 'timestamp as 'w)
- .groupBy('accountId, 'w)
- .select('accountId, 'w.start as 'timestamp, 'amount.sum)
- .insertInto("spend_report")
-{% endhighlight %}
-
-
-
-This defines your application as using one hour tumbling windows based on the timestamp column.
-So a row with timestamp `2019-06-01 01:23:47` is put in the `2019-06-01 01:00:00` window.
-
-Aggregations based on time are unique because time, as opposed to other attributes, generally moves forward in a continuous streaming application.
-In a batch context, windows offer a convenient API for grouping records by a timestamp attribute.
-
-Running the updated query will produce identical results as before.
-
-{% highlight raw %}
-# Query 2 output showing account id, timestamp, and amount
-
-> 1, 2019-01-01 00:00:00.0, $567.87
-> 2, 2019-01-01 00:00:00.0, $726.23
-> 1, 2019-01-01 01:00:00.0, $686.87
-> 2, 2019-01-01 01:00:00.0, $810.06
-> 1, 2019-01-01 02:00:00.0, $859.35
-> 2, 2019-01-01 02:00:00.0, $458.40
-> 1, 2019-01-01 03:00:00.0, $330.85
-> 2, 2019-01-01 03:00:00.0, $730.02
-> 1, 2019-01-01 04:00:00.0, $585.16
-> 2, 2019-01-01 04:00:00.0, $760.76
-{% endhighlight %}
-
-## Once More, With Streaming!
-
-Because Flink's Table API offers consistent syntax and semantics for both batch and streaming, migrating from one to the other requires just two steps.
-
-The first step is to replace the batch `ExecutionEnvironment` with its streaming counterpart, `StreamExecutionEnvironment`, which creates a continuous streaming Job.
-It includes stream-specific configurations, such as the time characteristic, which when set to [event time]({{ site.baseurl }}/dev/event_time.html) guarantees consistent results even when faced with out-of-order events or a Job failure.
-This is what will be used by your `Tumble` window when grouping records.
-
-
-
-The second step is to migrate from a bounded data source to an infinite data source.
-The project comes with an `UnboundedTransactionTableSource` that continuously creates transaction events in real-time.
-Similar to the `BoundedTransactionTableSource` this table is backed by data generated in memory to avoid any dependencies on external systems.
-In practice, this table might read from a streaming source such as Apache Kafka, AWS Kinesis, or Pravega.
-
-
-
-And that's it, a fully functional, stateful, distributed streaming application!
-The query continuously consumes the stream of transactions, computes the hourly spendings, and emits results as soon as they are ready.
-Since the input is unbounded, the query keeps running until it is manually stopped.
-And because the Job uses time window-based aggregations, Flink can perform specific optimizations such as state clean up when the framework knows that no more records will arrive for a particular window.
-
-{% highlight raw %}
-# Query 3 output showing account id, timestamp, and amount
-
-# These rows are calculated continuously over the hour
-# and output immediately at the end of the hour
-> 1, 2019-01-01 00:00:00.0, $567.87
-> 2, 2019-01-01 00:00:00.0, $726.23
-
-# Flink begins computing these rows as soon as
-# as the first record for the window arrives
-> 1, 2019-01-01 01:00:00.0, $686.87
-> 2, 2019-01-01 01:00:00.0, $810.06
-
-{% endhighlight %}
-
-## Final Application
-
-
-{% highlight scala %}
-package spendreport
-
-import org.apache.flink.streaming.api.scala.StreamExecutionEnvironment
-import org.apache.flink.streaming.api.TimeCharacteristic
-import org.apache.flink.table.api.Tumble
-import org.apache.flink.table.api.scala._
-import org.apache.flink.walkthrough.common.table._
-
-object SpendReport {
-
- def main(args: Array[String]): Unit = {
- val env = StreamExecutionEnvironment.getExecutionEnvironment
- env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime)
-
- val tEnv = StreamTableEnvironment.create(env)
-
- tEnv.registerTableSource("transactions", new UnboundedTransactionTableSource)
- tEnv.registerTableSink("spend_report", new SpendReportTableSink)
-
- tEnv
- .scan("transactions")
- .window(Tumble over 1.hour on 'timestamp as 'w)
- .groupBy('accountId, 'w)
- .select('accountId, 'w.start as 'timestamp, 'amount.sum)
- .insertInto("spend_report")
-
- env.execute("Spend Report")
- }
-}
-{% endhighlight %}
-
-
-
diff --git a/docs/index.md b/docs/index.md
index 9f0acae12d89f9..ae73a9c7ae13f2 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -23,51 +23,72 @@ specific language governing permissions and limitations
under the License.
-->
-This documentation is for Apache Flink version {{ site.version_title }}. These pages were built at: {% build_time %}.
+
+Apache Flink is a framework and distributed processing engine for stateful computations over _unbounded and bounded_ data streams. Flink has been designed to run in _all common cluster environments_, perform computations at _in-memory speed_ and at _any scale_.
+
+
+
+
+
+### Try Flink
+
+If you’re interested in playing around with Flink, try one of our tutorials:
+
+* [Local Installation]({% link try-flink/local_installation.md %})
+* [Fraud Detection with the DataStream API]({% link try-flink/datastream_api.md %})
+* [Real Time Reporting with the Table API]({% link try-flink/table_api.md %})
+* [Intro to the Python Table API]({% link try-flink/python_table_api.md %})
+* [Flink Operations Playground]({% link try-flink/flink-operations-playground.md %})
-Apache Flink is an open source platform for distributed stream and batch data processing. Flink’s core is a streaming dataflow engine that provides data distribution, communication, and fault tolerance for distributed computations over data streams. Flink builds batch processing on top of the streaming engine, overlaying native iteration support, managed memory, and program optimization.
+### Learn Flink
-## First Steps
+* To dive in deeper, the [Hands-on Training]({% link learn-flink/index.md %}) includes a set of lessons and exercises that provide a step-by-step introduction to Flink.
-* **Code Walkthroughs**: Follow step-by-step guides and implement a simple application or query in one of Flink's APIs.
- * [Implement a DataStream application](./getting-started/walkthroughs/datastream_api.html)
- * [Write a Table API query](./getting-started/walkthroughs/table_api.html)
+* The [Concepts]({% link concepts/index.md %}) section explains what you need to know about Flink before exploring the reference documentation.
-* **Docker Playgrounds**: Set up a sandboxed Flink environment in just a few minutes to explore and play with Flink.
- * [Run and manage Flink streaming applications](./getting-started/docker-playgrounds/flink-operations-playground.html)
+### Get Help with Flink
-* **Concepts**: Learn about Flink's basic concepts to better understand the documentation.
- * [Dataflow Programming Model](concepts/programming-model.html)
- * [Distributed Runtime](concepts/runtime.html)
- * [Glossary](concepts/glossary.html)
+If you get stuck, check out our [community support resources](https://flink.apache.org/community.html). In particular, Apache Flink’s user mailing list is consistently ranked as one of the most active of any Apache project, and is a great way to get help quickly.
-## API References
+
+
-The API references list and explain all features of Flink's APIs.
+### Explore Flink
-* [DataStream API](dev/datastream_api.html)
-* [DataSet API](dev/batch/index.html)
-* [Table API & SQL](dev/table/index.html)
+The reference documentation covers all the details. Some starting points:
-## Deployment
+
+
-Before putting your Flink job into production, read the [Production Readiness Checklist](ops/production_ready.html).
+* [DataStream API]({% link dev/datastream_api.md %})
+* [Table API & SQL]({% link dev/table/index.md %})
+* [Stateful Functions]({% if site.is_stable %} {{ site.statefundocs_stable_baseurl }} {% else %} {{ site.statefundocs_baseurl }} {% endif %})
-## Release Notes
+
+
-Release notes cover important changes between Flink versions. Please carefully read these notes if you plan to upgrade your Flink setup to a later version.
+* [Configuration]({% link ops/config.md %})
+* [Rest API]({% link monitoring/rest_api.md %})
+* [CLI]({% link ops/cli.md %})
-* [Release notes for Flink 1.10](release-notes/flink-1.10.html).
-* [Release notes for Flink 1.9](release-notes/flink-1.9.html).
-* [Release notes for Flink 1.8](release-notes/flink-1.8.html).
-* [Release notes for Flink 1.7](release-notes/flink-1.7.html).
-* [Release notes for Flink 1.6](release-notes/flink-1.6.html).
-* [Release notes for Flink 1.5](release-notes/flink-1.5.html).
+
+
-## External Resources
+### Deploy Flink
-- **Flink Forward**: Talks from past conferences are available at the [Flink Forward](http://flink-forward.org/) website and on [YouTube](https://www.youtube.com/channel/UCY8_lgiZLZErZPF47a2hXMA). [Robust Stream Processing with Apache Flink](http://2016.flink-forward.org/kb_sessions/robust-stream-processing-with-apache-flink/) is a good place to start.
+Before putting your Flink job into production, read the [Production Readiness Checklist]({% link ops/production_ready.md %}). For an overview of possible deployment targets, see [Clusters and Deployments]({% link ops/deployment/index.md %}).
-- **Training**: The [training materials](https://training.ververica.com/) from Ververica include slides, exercises, and sample solutions.
+### Upgrade Flink
-- **Blogs**: The [Apache Flink](https://flink.apache.org/blog/) and [Ververica](https://www.ververica.com/blog) blogs publish frequent, in-depth technical articles about Flink.
+Release notes cover important changes between Flink versions. Please read them carefully if you plan to upgrade your Flink setup.
+
+See the release notes for [Flink 1.11]({% link release-notes/flink-1.11.md %}), [Flink 1.10]({% link release-notes/flink-1.10.md %}), [Flink 1.9]({% link release-notes/flink-1.9.md %}), [Flink 1.8]({% link release-notes/flink-1.8.md %}), or [Flink 1.7]({% link release-notes/flink-1.7.md %}).
+
+
+
+
+
+
+This documentation is for Apache Flink version {{ site.version_title }}. These pages were built at: {% build_time %}.
+
+
diff --git a/docs/index.zh.md b/docs/index.zh.md
index 2315024829eabe..f9a796657c83fe 100644
--- a/docs/index.zh.md
+++ b/docs/index.zh.md
@@ -23,53 +23,72 @@ specific language governing permissions and limitations
under the License.
-->
+
diff --git a/docs/internals/task_lifecycle.md b/docs/internals/task_lifecycle.md
index 44f847f0cba6d5..ee81780c3c3ebe 100644
--- a/docs/internals/task_lifecycle.md
+++ b/docs/internals/task_lifecycle.md
@@ -92,7 +92,7 @@ operator is opened and before it is closed. The responsibility of this method is
to the specified [state backend]({{ site.baseurl }}/ops/state/state_backends.html) from where it will be retrieved when
the job resumes execution after a failure. Below we include a brief description of Flink's checkpointing mechanism,
and for a more detailed discussion on the principles around checkpointing in Flink please read the corresponding documentation:
-[Data Streaming Fault Tolerance]({{ site.baseurl }}/internals/stream_checkpointing.html).
+[Data Streaming Fault Tolerance]({{ site.baseurl }}/learn-flink/fault_tolerance.html).
## Task Lifecycle
diff --git a/docs/internals/task_lifecycle.zh.md b/docs/internals/task_lifecycle.zh.md
index 7de935b4ed88f8..a06ec4ecebd685 100644
--- a/docs/internals/task_lifecycle.zh.md
+++ b/docs/internals/task_lifecycle.zh.md
@@ -92,7 +92,7 @@ operator is opened and before it is closed. The responsibility of this method is
to the specified [state backend]({{ site.baseurl }}/ops/state/state_backends.html) from where it will be retrieved when
the job resumes execution after a failure. Below we include a brief description of Flink's checkpointing mechanism,
and for a more detailed discussion on the principles around checkpointing in Flink please read the corresponding documentation:
-[Data Streaming Fault Tolerance]({{ site.baseurl }}/internals/stream_checkpointing.html).
+[Data Streaming Fault Tolerance]({{ site.baseurl }}/learn-flink/fault_tolerance.html).
## Task Lifecycle
diff --git a/docs/learn-flink/datastream_api.md b/docs/learn-flink/datastream_api.md
new file mode 100644
index 00000000000000..23f1f802f145a9
--- /dev/null
+++ b/docs/learn-flink/datastream_api.md
@@ -0,0 +1,236 @@
+---
+title: Intro to the DataStream API
+nav-id: datastream-api
+nav-pos: 2
+nav-title: Intro to the DataStream API
+nav-parent_id: learn-flink
+---
+
+
+The focus of this training is to broadly cover the DataStream API well enough that you will be able
+to get started writing streaming applications.
+
+* This will be replaced by the TOC
+{:toc}
+
+## What can be Streamed?
+
+Flink's DataStream APIs for Java and Scala will let you stream anything they can serialize. Flink's
+own serializer is used for
+
+- basic types, i.e., String, Long, Integer, Boolean, Array
+- composite types: Tuples, POJOs, and Scala case classes
+
+and Flink falls back to Kryo for other types. It is also possible to use other serializers with
+Flink. Avro, in particular, is well supported.
+
+### Java tuples and POJOs
+
+Flink's native serializer can operate efficiently on tuples and POJOs.
+
+#### Tuples
+
+For Java, Flink defines its own `Tuple0` thru `Tuple25` types.
+
+{% highlight java %}
+Tuple2 person = Tuple2.of("Fred", 35);
+
+// zero based index!
+String name = person.f0;
+Integer age = person.f1;
+{% endhighlight %}
+
+#### POJOs
+
+Flink recognizes a data type as a POJO type (and allows “by-name” field referencing) if the following conditions are fulfilled:
+
+- The class is public and standalone (no non-static inner class)
+- The class has a public no-argument constructor
+- All non-static, non-transient fields in the class (and all superclasses) are either public (and
+ non-final) or have public getter- and setter- methods that follow the Java beans naming
+ conventions for getters and setters.
+
+Example:
+
+{% highlight java %}
+public class Person {
+ public String name;
+ public Integer age;
+ public Person() {};
+ public Person(String name, Integer age) {
+ . . .
+ };
+}
+
+Person person = new Person("Fred Flintstone", 35);
+{% endhighlight %}
+
+Flink's serializer [supports schema evolution for POJO types]({% link dev/stream/state/schema_evolution.md %}#pojo-types).
+
+### Scala tuples and case classes
+
+These work just as you'd expect.
+
+{% top %}
+
+## A Complete Example
+
+This example takes a stream of records about people as input, and filters it to only include the adults.
+
+{% highlight java %}
+import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
+import org.apache.flink.streaming.api.datastream.DataStream;
+import org.apache.flink.api.common.functions.FilterFunction;
+
+public class Example {
+
+ public static void main(String[] args) throws Exception {
+ final StreamExecutionEnvironment env =
+ StreamExecutionEnvironment.getExecutionEnvironment();
+
+ DataStream flintstones = env.fromElements(
+ new Person("Fred", 35),
+ new Person("Wilma", 35),
+ new Person("Pebbles", 2));
+
+ DataStream adults = flintstones.filter(new FilterFunction() {
+ @Override
+ public boolean filter(Person person) throws Exception {
+ return person.age >= 18;
+ }
+ });
+
+ adults.print();
+
+ env.execute();
+ }
+
+ public static class Person {
+ public String name;
+ public Integer age;
+ public Person() {};
+
+ public Person(String name, Integer age) {
+ this.name = name;
+ this.age = age;
+ };
+
+ public String toString() {
+ return this.name.toString() + ": age " + this.age.toString();
+ };
+ }
+}
+{% endhighlight %}
+
+### Stream execution environment
+
+Every Flink application needs an execution environment, `env` in this example. Streaming
+applications need to use a `StreamExecutionEnvironment`.
+
+The DataStream API calls made in your application build a job graph that is attached to the
+`StreamExecutionEnvironment`. When `env.execute()` is called this graph is packaged up and sent to
+the JobManager, which parallelizes the job and distributes slices of it to the Task Managers for
+execution. Each parallel slice of your job will be executed in a *task slot*.
+
+Note that if you don't call execute(), your application won't be run.
+
+
+
+This distributed runtime depends on your application being serializable. It also requires that all
+dependencies are available to each node in the cluster.
+
+### Basic stream sources
+
+The example above constructs a `DataStream` using `env.fromElements(...)`. This is a
+convenient way to throw together a simple stream for use in a prototype or test. There is also a
+`fromCollection(Collection)` method on `StreamExecutionEnvironment`. So instead, you could do this:
+
+{% highlight java %}
+List people = new ArrayList();
+
+people.add(new Person("Fred", 35));
+people.add(new Person("Wilma", 35));
+people.add(new Person("Pebbles", 2));
+
+DataStream flintstones = env.fromCollection(people);
+{% endhighlight %}
+
+Another convenient way to get some data into a stream while prototyping is to use a socket
+
+{% highlight java %}
+DataStream lines = env.socketTextStream("localhost", 9999)
+{% endhighlight %}
+
+or a file
+
+{% highlight java %}
+DataStream lines = env.readTextFile("file:///path");
+{% endhighlight %}
+
+In real applications the most commonly used data sources are those that support low-latency, high
+throughput parallel reads in combination with rewind and replay -- the prerequisites for high
+performance and fault tolerance -- such as Apache Kafka, Kinesis, and various filesystems. REST APIs
+and databases are also frequently used for stream enrichment.
+
+### Basic stream sinks
+
+The example above uses `adults.print()` to print its results to the task manager logs (which will
+appear in your IDE's console, when running in an IDE). This will call `toString()` on each element
+of the stream.
+
+The output looks something like this
+
+ 1> Fred: age 35
+ 2> Wilma: age 35
+
+where 1> and 2> indicate which sub-task (i.e., thread) produced the output.
+
+In production, commonly used sinks include the StreamingFileSink, various databases,
+and several pub-sub systems.
+
+### Debugging
+
+In production, your application will run in a remote cluster or set of containers. And if it fails,
+it will fail remotely. The JobManager and TaskManager logs can be very helpful in debugging such
+failures, but it is much easier to do local debugging inside an IDE, which is something that Flink
+supports. You can set breakpoints, examine local variables, and step through your code. You can also
+step into Flink's code, which can be a great way to learn more about its internals if you are
+curious to see how Flink works.
+
+{% top %}
+
+## Hands-on
+
+At this point you know enough to get started coding and running a simple DataStream application.
+Clone the [flink-training repo](https://github.com/apache/flink-training/tree/{% if site.is_stable %}release-{{ site.version_title }}{% else %}master{% endif %}), and after following the
+instructions in the README, do the first exercise:
+[Filtering a Stream (Ride Cleansing)](https://github.com/apache/flink-training/tree/{% if site.is_stable %}release-{{ site.version_title }}{% else %}master{% endif %}/ride-cleansing).
+
+{% top %}
+
+## Further Reading
+
+- [Flink Serialization Tuning Vol. 1: Choosing your Serializer — if you can](https://flink.apache.org/news/2020/04/15/flink-serialization-tuning-vol-1.html)
+- [Anatomy of a Flink Program]({% link dev/datastream_api.md %}#anatomy-of-a-flink-program)
+- [Data Sources]({% link dev/datastream_api.md %}#data-sources)
+- [Data Sinks]({% link dev/datastream_api.md %}#data-sinks)
+- [DataStream Connectors]({% link dev/connectors/index.md %})
+
+{% top %}
diff --git a/docs/learn-flink/datastream_api.zh.md b/docs/learn-flink/datastream_api.zh.md
new file mode 100644
index 00000000000000..92dfbdaeee5158
--- /dev/null
+++ b/docs/learn-flink/datastream_api.zh.md
@@ -0,0 +1,213 @@
+---
+title: DataStream API 简介
+nav-id: datastream-api
+nav-pos: 2
+nav-title: DataStream API 简介
+nav-parent_id: learn-flink
+---
+
+
+该练习的重点是充分全面地了解 DataStream API,以便于编写流式应用入门。
+
+* This will be replaced by the TOC
+{:toc}
+
+## 什么能被转化成流?
+
+Flink 的 Java 和 Scala DataStream API 可以将任何可序列化的对象转化为流。Flink 自带的序列化器有
+
+- 基本类型,即 String、Long、Integer、Boolean、Array
+- 复合类型:Tuples、POJOs 和 Scala case classes
+
+而且 Flink 会交给 Kryo 序列化其他类型。也可以将其他序列化器和 Flink 一起使用。特别是有良好支持的 Avro。
+
+### Java tuples 和 POJOs
+
+Flink 的原生序列化器可以高效地操作 tuples 和 POJOs
+
+#### Tuples
+
+对于 Java,Flink 自带有 `Tuple0` 到 `Tuple25` 类型。
+
+{% highlight java %}
+Tuple2 person = Tuple2.of("Fred", 35);
+
+// zero based index!
+String name = person.f0;
+Integer age = person.f1;
+{% endhighlight %}
+
+#### POJOs
+
+如果满足以下条件,Flink 将数据类型识别为 POJO 类型(并允许“按名称”字段引用):
+
+- 该类是公有且独立的(没有非静态内部类)
+- 该类有公有的无参构造函数
+- 类(及父类)中所有的所有不被 static、transient 修饰的属性要么是公有的(且不被 final 修饰),要么是包含公有的 getter 和 setter 方法,这些方法遵循 Java bean 命名规范。
+
+示例:
+
+{% highlight java %}
+public class Person {
+ public String name;
+ public Integer age;
+ public Person() {};
+ public Person(String name, Integer age) {
+ . . .
+ };
+}
+
+Person person = new Person("Fred Flintstone", 35);
+{% endhighlight %}
+
+Flink 的序列化器[支持的 POJO 类型数据结构升级]({% link dev/stream/state/schema_evolution.zh.md %}#pojo-types)。
+
+### Scala tuples 和 case classes
+
+如果你了解 Scala,那一定知道 tuple 和 case class。
+
+{% top %}
+
+## 一个完整的示例
+
+该示例将关于人的记录流作为输入,并且过滤后只包含成年人。
+
+{% highlight java %}
+import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
+import org.apache.flink.streaming.api.datastream.DataStream;
+import org.apache.flink.api.common.functions.FilterFunction;
+
+public class Example {
+
+ public static void main(String[] args) throws Exception {
+ final StreamExecutionEnvironment env =
+ StreamExecutionEnvironment.getExecutionEnvironment();
+
+ DataStream flintstones = env.fromElements(
+ new Person("Fred", 35),
+ new Person("Wilma", 35),
+ new Person("Pebbles", 2));
+
+ DataStream adults = flintstones.filter(new FilterFunction() {
+ @Override
+ public boolean filter(Person person) throws Exception {
+ return person.age >= 18;
+ }
+ });
+
+ adults.print();
+
+ env.execute();
+ }
+
+ public static class Person {
+ public String name;
+ public Integer age;
+ public Person() {};
+
+ public Person(String name, Integer age) {
+ this.name = name;
+ this.age = age;
+ };
+
+ public String toString() {
+ return this.name.toString() + ": age " + this.age.toString();
+ };
+ }
+}
+{% endhighlight %}
+
+### Stream 执行环境
+
+每个 Flink 应用都需要有执行环境,在该示例中为 `env`。流式应用需要用到 `StreamExecutionEnvironment`。
+
+DataStream API 将你的应用构建为一个 job graph,并附加到 `StreamExecutionEnvironment` 。当调用 `env.execute()` 时此 graph 就被打包并发送到 JobManager 上,后者对作业并行处理并将其子任务分发给 Task Manager 来执行。每个作业的并行子任务将在 *task slot* 中执行。
+
+注意,如果没有调用 execute(),应用就不会运行。
+
+
+
+此分布式运行时取决于你的应用是否是可序列化的。它还要求所有依赖对集群中的每个节点均可用。
+
+### 基本的 stream source
+
+上述示例用 `env.fromElements(...)` 方法构造 `DataStream` 。这样将简单的流放在一起是为了方便用于原型或测试。`StreamExecutionEnvironment` 上还有一个 `fromCollection(Collection)` 方法。因此,你可以这样做:
+
+{% highlight java %}
+List people = new ArrayList();
+
+people.add(new Person("Fred", 35));
+people.add(new Person("Wilma", 35));
+people.add(new Person("Pebbles", 2));
+
+DataStream flintstones = env.fromCollection(people);
+{% endhighlight %}
+
+另一个获取数据到流中的便捷方法是用 socket
+
+{% highlight java %}
+DataStream lines = env.socketTextStream("localhost", 9999)
+{% endhighlight %}
+
+或读取文件
+
+{% highlight java %}
+DataStream lines = env.readTextFile("file:///path");
+{% endhighlight %}
+
+在真实的应用中,最常用的数据源是那些支持低延迟,高吞吐并行读取以及重复(高性能和容错能力为先决条件)的数据源,例如 Apache Kafka,Kinesis 和各种文件系统。REST API 和数据库也经常用于增强流处理的能力(stream enrichment)。
+
+### 基本的 stream sink
+
+上述示例用 `adults.print()` 打印其结果到 task manager 的日志中(如果运行在 IDE 中时,将追加到你的 IDE 控制台)。它会对流中的每个元素都调用 `toString()` 方法。
+
+输出看起来类似于
+
+ 1> Fred: age 35
+ 2> Wilma: age 35
+
+1> 和 2> 指出输出来自哪个 sub-task(即 thread)
+
+In production, commonly used sinks include the StreamingFileSink, various databases,
+and several pub-sub systems.
+
+### 调试
+
+在生产中,应用程序将在远程集群或一组容器中运行。如果集群或容器挂了,这就属于远程失败。JobManager 和 TaskManager 日志对于调试此类故障非常有用,但是更简单的是 Flink 支持在 IDE 内部进行本地调试。你可以设置断点,检查局部变量,并逐行执行代码。如果想了解 Flink 的工作原理和内部细节,查看 Flink 源码也是非常好的方法。
+
+{% top %}
+
+## 动手实践
+
+至此,你已经可以开始编写并运行一个简单的 DataStream 应用了。
+克隆 [flink-training repo](https://github.com/apache/flink-training/tree/{% if site.is_stable %}release-{{ site.version_title }}{% else %}master{% endif %}) 并在阅读完 README 中的指示后,开始尝试第一个练习吧:
+[Filtering a Stream (Ride Cleansing)](https://github.com/apache/flink-training/tree/{% if site.is_stable %}release-{{ site.version_title }}{% else %}master{% endif %}/ride-cleansing)。
+
+{% top %}
+
+## 更多阅读
+
+- [Flink Serialization Tuning Vol. 1: Choosing your Serializer — if you can](https://flink.apache.org/news/2020/04/15/flink-serialization-tuning-vol-1.html)
+- [Anatomy of a Flink Program]({% link dev/datastream_api.zh.md %}#anatomy-of-a-flink-program)
+- [Data Sources]({% link dev/datastream_api.zh.md %}#data-sources)
+- [Data Sinks]({% link dev/datastream_api.zh.md %}#data-sinks)
+- [DataStream Connectors]({% link dev/connectors/index.zh.md %})
+
+{% top %}
diff --git a/docs/training/etl.md b/docs/learn-flink/etl.md
similarity index 99%
rename from docs/training/etl.md
rename to docs/learn-flink/etl.md
index 9337be315447b7..29cbdbfd269014 100644
--- a/docs/training/etl.md
+++ b/docs/learn-flink/etl.md
@@ -3,7 +3,7 @@ title: Data Pipelines & ETL
nav-id: etl
nav-pos: 3
nav-title: Data Pipelines & ETL
-nav-parent_id: training
+nav-parent_id: learn-flink
---
+
+* This will be replaced by the TOC
+{:toc}
+
+## State Backends
+
+由 Flink 管理的 keyed state 是一种分片的键/值存储,每个 keyed state 的工作副本都保存在负责该键的 taskmanager 本地中。另外,Operator state 也保存在机器节点本地。Flink 定期获取所有状态的快照,并将这些快照复制到持久化的位置,例如分布式文件系统。
+
+如果发生故障,Flink 可以恢复应用程序的完整状态并继续处理,就如同没有出现过异常。
+
+Flink 管理的状态存储在 _state backend_ 中。Flink 有两种 state backend 的实现 -- 一种基于 RocksDB 内嵌 key/value 存储将其工作状态保存在磁盘上的,另一种基于堆的 state backend,将其工作状态保存在 Java 的堆内存中。这种基于堆的 state backend 有两种类型:FsStateBackend,将其状态快照持久化到分布式文件系统;MemoryStateBackend,它使用 JobManager 的堆保存状态快照。
+
+
+
+
+
名称
+
Working State
+
状态备份
+
快照
+
+
+
+
+
RocksDBStateBackend
+
本地磁盘(tmp dir)
+
分布式文件系统
+
全量 / 增量
+
+
+
+
+
支持大于内存大小的状态
+
经验法则:比基于堆的后端慢10倍
+
+
+
+
+
FsStateBackend
+
JVM Heap
+
分布式文件系统
+
全量
+
+
+
+
+
快速,需要大的堆内存
+
受限制于 GC
+
+
+
+
+
MemoryStateBackend
+
JVM Heap
+
JobManager JVM Heap
+
全量
+
+
+
+
+
适用于小状态(本地)的测试和实验
+
+
+
+
+
+
+当使用基于堆的 state backend 保存状态时,访问和更新涉及在堆上读写对象。但是对于保存在 `RocksDBStateBackend` 中的对象,访问和更新涉及序列化和反序列化,所以会有更大的开销。但 RocksDB 的状态量仅受本地磁盘大小的限制。还要注意,只有 `RocksDBStateBackend` 能够进行增量快照,这对于具有大量变化缓慢状态的应用程序来说是大有裨益的。
+
+所有这些 state backends 都能够异步执行快照,这意味着它们可以在不妨碍正在进行的流处理的情况下执行快照。
+
+{% top %}
+
+## 状态快照
+
+### 定义
+
+* _快照_ -- 是 Flink 作业状态全局一致镜像的通用术语。快照包括指向每个数据源的指针(例如,到文件或 Kafka 分区的偏移量)以及每个作业的有状态运算符的状态副本,该状态副本是处理了 sources 偏移位置之前所有的事件后而生成的状态。
+
+* _Checkpoint_ -- 一种由 Flink 自动执行的快照,其目的是能够从故障中恢复。Checkpoints 可以是增量的,并为快速恢复进行了优化。
+
+* _外部化的 Checkpoint_ -- 通常 checkpoints 不会被用户操纵。Flink 只保留作业运行时的最近的 _n_ 个 checkpoints(_n_ 可配置),并在作业取消时删除它们。但你可以将它们配置为保留,在这种情况下,你可以手动从中恢复。
+
+* _Savepoint_ -- 用户出于某种操作目的(例如有状态的重新部署/升级/缩放操作)手动(或 API 调用)触发的快照。Savepoints 始终是完整的,并且已针对操作灵活性进行了优化。
+
+### 状态快照如何工作?
+
+Flink 使用 [Chandy-Lamport algorithm](https://en.wikipedia.org/wiki/Chandy-Lamport_algorithm) 算法的一种变体,称为异步 barrier 快照(_asynchronous barrier snapshotting_)。
+
+当 checkpoint coordinator(job manager 的一部分)指示 task manager 开始 checkpoint 时,它会让所有 sources 记录它们的偏移量,并将编号的 _checkpoint barriers_ 插入到它们的流中。这些 barriers 流经 job graph,标注每个 checkpoint 前后的流部分。
+
+
+
+Checkpoint _n_ 将包含每个 operator 的 state,这些 state 是对应的 operator 消费了**严格在 checkpoint barrier _n_ 之前的所有事件,并且不包含在此(checkpoint barrier _n_)后的任何事件**后而生成的状态。
+
+当 job graph 中的每个 operator 接收到 barriers 时,它就会记录下其状态。拥有两个输入流的 Operators(例如 `CoProcessFunction`)会执行 _barrier 对齐(barrier alignment)_ 以便当前快照能够包含消费两个输入流 barrier 之前(但不超过)的所有 events 而产生的状态。
+
+
+
+Flink 的 state backends 利用写时复制(copy-on-write)机制允许当异步生成旧版本的状态快照时,能够不受影响地继续流处理。只有当快照被持久保存后,这些旧版本的状态才会被当做垃圾回收。
+
+### 确保精确一次(exactly once)
+
+当流处理应用程序发生错误的时候,结果可能会产生丢失或者重复。Flink 根据你为应用程序和集群的配置,可以产生以下结果:
+
+- Flink 不会从快照中进行恢复(_at most once_)
+- 没有任何丢失,但是你可能会得到重复冗余的结果(_at least once_)
+- 没有丢失或冗余重复(_exactly once_)
+
+Flink 通过回退和重新发送 source 数据流从故障中恢复,当理想情况被描述为**精确一次**时,这并*不*意味着每个事件都将被精确一次处理。相反,这意味着 _每一个事件都会影响 Flink 管理的状态精确一次_。
+
+Barrier 只有在需要提供精确一次的语义保证时需要进行对齐(Barrier alignment)。如果不需要这种语义,可以通过配置 `CheckpointingMode.AT_LEAST_ONCE` 关闭 Barrier 对齐来提高性能。
+
+### 端到端精确一次
+
+为了实现端到端的精确一次,以便 sources 中的每个事件都仅精确一次对 sinks 生效,必须满足以下条件:
+
+1. 你的 sources 必须是可重放的,并且
+2. 你的 sinks 必须是事务性的(或幂等的)
+
+{% top %}
+
+## 实践练习
+
+[Flink Operations Playground]({% link try-flink/flink-operations-playground.zh.md %}) 包括有关 [Observing Failure & Recovery]({% link try-flink/flink-operations-playground.zh.md %}#observing-failure--recovery) 的部分。
+
+{% top %}
+
+## 延伸阅读
+
+- [Stateful Stream Processing]({% link concepts/stateful-stream-processing.zh.md %})
+- [State Backends]({% link ops/state/state_backends.zh.md %})
+- [Data Sources 和 Sinks 的容错保证]({% link dev/connectors/guarantees.zh.md %})
+- [开启和配置 Checkpointing]({% link dev/stream/state/checkpointing.zh.md %})
+- [Checkpoints]({% link ops/state/checkpoints.zh.md %})
+- [Savepoints]({% link ops/state/savepoints.zh.md %})
+- [大状态与 Checkpoint 调优]({% link ops/state/large_state_tuning.zh.md %})
+- [监控 Checkpoint]({% link monitoring/checkpoint_monitoring.zh.md %})
+- [Task 故障恢复]({% link dev/task_failure_recovery.zh.md %})
+
+{% top %}
diff --git a/docs/learn-flink/index.md b/docs/learn-flink/index.md
new file mode 100644
index 00000000000000..873c0204abfba4
--- /dev/null
+++ b/docs/learn-flink/index.md
@@ -0,0 +1,185 @@
+---
+title: "Learn Flink: Hands-on Training"
+nav-id: learn-flink
+nav-pos: 2
+nav-title: ' Learn Flink'
+nav-parent_id: root
+nav-show_overview: true
+---
+
+
+* This will be replaced by the TOC
+{:toc}
+
+## Goals and Scope of this Training
+
+This training presents an introduction to Apache Flink that includes just enough to get you started
+writing scalable streaming ETL, analytics, and event-driven applications, while leaving out a lot of
+(ultimately important) details. The focus is on providing straightforward introductions to Flink's
+APIs for managing state and time, with the expectation that having mastered these fundamentals,
+you'll be much better equipped to pick up the rest of what you need to know from the more detailed
+reference documentation. The links at the end of each section will lead you to where you
+can learn more.
+
+Specifically, you will learn:
+
+- how to implement streaming data processing pipelines
+- how and why Flink manages state
+- how to use event time to consistently compute accurate analytics
+- how to build event-driven applications on continuous streams
+- how Flink is able to provide fault-tolerant, stateful stream processing with exactly-once semantics
+
+This training focuses on four critical concepts: continuous processing of streaming data, event
+time, stateful stream processing, and state snapshots. This page introduces these concepts.
+
+{% info Note %} Accompanying this training is a set of hands-on exercises that will
+guide you through learning how to work with the concepts being presented. A link to the relevant
+exercise is provided at the end of each section.
+
+{% top %}
+
+## Stream Processing
+
+Streams are data's natural habitat. Whether it is events from web servers, trades from a stock
+exchange, or sensor readings from a machine on a factory floor, data is created as part of a stream.
+But when you analyze data, you can either organize your processing around _bounded_ or _unbounded_
+streams, and which of these paradigms you choose has profound consequences.
+
+
+
+**Batch processing** is the paradigm at work when you process a bounded data stream. In this mode of
+operation you can choose to ingest the entire dataset before producing any results, which means that
+it is possible, for example, to sort the data, compute global statistics, or produce a final report
+that summarizes all of the input.
+
+**Stream processing**, on the other hand, involves unbounded data streams. Conceptually, at least,
+the input may never end, and so you are forced to continuously process the data as it arrives.
+
+In Flink, applications are composed of **streaming dataflows** that may be transformed by
+user-defined **operators**. These dataflows form directed graphs that start with one or more
+**sources**, and end in one or more **sinks**.
+
+
+
+Often there is a one-to-one correspondence between the transformations in the program and the
+operators in the dataflow. Sometimes, however, one transformation may consist of multiple operators.
+
+An application may consume real-time data from streaming sources such as message queues or
+distributed logs, like Apache Kafka or Kinesis. But flink can also consume bounded, historic data
+from a variety of data sources. Similarly, the streams of results being produced by a Flink
+application can be sent to a wide variety of systems that can be connected as sinks.
+
+
+
+### Parallel Dataflows
+
+Programs in Flink are inherently parallel and distributed. During execution, a
+*stream* has one or more **stream partitions**, and each *operator* has one or
+more **operator subtasks**. The operator subtasks are independent of one
+another, and execute in different threads and possibly on different machines or
+containers.
+
+The number of operator subtasks is the **parallelism** of that particular
+operator.
+Different operators of the same program may have different levels of
+parallelism.
+
+
+
+Streams can transport data between two operators in a *one-to-one* (or
+*forwarding*) pattern, or in a *redistributing* pattern:
+
+ - **One-to-one** streams (for example between the *Source* and the *map()*
+ operators in the figure above) preserve the partitioning and ordering of
+ the elements. That means that subtask[1] of the *map()* operator will see
+ the same elements in the same order as they were produced by subtask[1] of
+ the *Source* operator.
+
+ - **Redistributing** streams (as between *map()* and *keyBy/window* above, as
+ well as between *keyBy/window* and *Sink*) change the partitioning of
+ streams. Each *operator subtask* sends data to different target subtasks,
+ depending on the selected transformation. Examples are *keyBy()* (which
+ re-partitions by hashing the key), *broadcast()*, or *rebalance()* (which
+ re-partitions randomly). In a *redistributing* exchange the ordering among
+ the elements is only preserved within each pair of sending and receiving
+ subtasks (for example, subtask[1] of *map()* and subtask[2] of
+ *keyBy/window*). So, for example, the redistribution between the keyBy/window and
+ the Sink operators shown above introduces non-determinism regarding the
+ order in which the aggregated results for different keys arrive at the Sink.
+
+{% top %}
+
+## Timely Stream Processing
+
+For most streaming applications it is very valuable to be able re-process historic data with the
+same code that is used to process live data -- and to produce deterministic, consistent results,
+regardless.
+
+It can also be crucial to pay attention to the order in which events occurred, rather than the order
+in which they are delivered for processing, and to be able to reason about when a set of events is
+(or should be) complete. For example, consider the set of events involved in an e-commerce
+transaction, or financial trade.
+
+These requirements for timely stream processing can be met by using event time timestamps that are
+recorded in the data stream, rather than using the clocks of the machines processing the data.
+
+{% top %}
+
+## Stateful Stream Processing
+
+Flink's operations can be stateful. This means that how one event is handled can depend on the
+accumulated effect of all the events that came before it. State may be used for something simple,
+such as counting events per minute to display on a dashboard, or for something more complex, such as
+computing features for a fraud detection model.
+
+A Flink application is run in parallel on a distributed cluster. The various parallel instances of a
+given operator will execute independently, in separate threads, and in general will be running on
+different machines.
+
+The set of parallel instances of a stateful operator is effectively a sharded key-value store. Each
+parallel instance is responsible for handling events for a specific group of keys, and the state for
+those keys is kept locally.
+
+The diagram below shows a job running with a parallelism of two across the first three operators in
+the job graph, terminating in a sink that has a parallelism of one. The third operator is stateful,
+and you can see that a fully-connected network shuffle is occurring between the second and third
+operators. This is being done to partition the stream by some key, so that all of the events that
+need to be processed together, will be.
+
+
+
+State is always accessed locally, which helps Flink applications achieve high throughput and
+low-latency. You can choose to keep state on the JVM heap, or if it is too large, in efficiently
+organized on-disk data structures.
+
+
+
+{% top %}
+
+## Fault Tolerance via State Snapshots
+
+Flink is able to provide fault-tolerant, exactly-once semantics through a combination of state
+snapshots and stream replay. These snapshots capture the entire state of the distributed pipeline,
+recording offsets into the input queues as well as the state throughout the job graph that has
+resulted from having ingested the data up to that point. When a failure occurs, the sources are
+rewound, the state is restored, and processing is resumed. As depicted above, these state snapshots
+are captured asynchronously, without impeding the ongoing processing.
+
+{% top %}
diff --git a/docs/learn-flink/index.zh.md b/docs/learn-flink/index.zh.md
new file mode 100644
index 00000000000000..75be8be376e6ff
--- /dev/null
+++ b/docs/learn-flink/index.zh.md
@@ -0,0 +1,117 @@
+---
+title: 实践练习
+nav-id: learn-flink
+nav-pos: 2
+nav-title: ' 实践练习'
+nav-parent_id: root
+nav-show_overview: true
+---
+
+
+* This will be replaced by the TOC
+{:toc}
+
+## 本章教程的目标及涵盖范围
+
+本章教程对 Apache Flink 的基本概念进行了介绍,虽然省略了许多重要细节,但是如果你掌握了本章内容,就足以实现可扩展并行度的 ETL、数据分析以及事件驱动的流式应用程序。本章重点对 Flink API 中的状态管理和时间进行了介绍,掌握了这些基础知识后,你将能更好地从其他详细参考文档中获取和掌握你所需要的知识。每小节结尾都有链接去引导你了解更多内容。
+
+具体来说,你将在本章学习到以下内容:
+
+- 如何实现流数据处理管道(pipelines)
+- Flink 如何管理状态以及为何需要管理状态
+- 如何使用事件时间(event time)来一致并准确地进行计算分析
+- 如何在源源不断的数据流上构建事件驱动的应用程序
+- Flink 如何提供具有精确一次(exactly-once)计算语义的可容错、有状态流处理
+
+本章教程着重介绍四个概念:源源不断的流式数据处理、事件时间、有状态流处理和状态快照。基本概念介绍如下。
+
+{% info Note %} 每小节教程都有实践练习引导你如何在程序中使用其所述的概念,并在小节结尾都提供了相关实践练习的代码链接。
+
+{% top %}
+
+## 流处理
+
+在自然环境中,数据的产生原本就是流式的。无论是来自 Web 服务器的事件数据,证券交易所的交易数据,还是来自工厂车间机器上的传感器数据,其数据都是流式的。但是当你分析数据时,可以围绕 _有界流_(_bounded_)或 _无界流_(_unbounded_)两种模型来组织处理数据,当然,选择不同的模型,程序的执行和处理方式也都会不同。
+
+
+
+**批处理**是有界数据流处理的范例。在这种模式下,你可以选择在计算结果输出之前输入整个数据集,这也就意味着你可以对整个数据集的数据进行排序、统计或汇总计算后再输出结果。
+
+**流处理**正相反,其涉及无界数据流。至少理论上来说,它的数据输入永远不会结束,因此程序必须持续不断地对到达的数据进行处理。
+
+在 Flink 中,应用程序由用户自定义**算子**转换而来的**流式 dataflows** 所组成。这些流式 dataflows 形成了有向图,以一个或多个**源**(source)开始,并以一个或多个**汇**(sink)结束。
+
+
+
+通常,程序代码中的 transformation 和 dataflow 中的算子(operator)之间是一一对应的。但有时也会出现一个 transformation 包含多个算子的情况,如上图所示。
+
+Flink 应用程序可以消费来自消息队列或分布式日志这类流式数据源(例如 Apache Kafka 或 Kinesis)的实时数据,也可以从各种的数据源中消费有界的历史数据。同样,Flink 应用程序生成的结果流也可以发送到各种数据汇中。
+
+
+
+### 并行 Dataflows
+
+Flink 程序本质上是分布式并行程序。在程序执行期间,一个流有一个或多个**流分区**(Stream Partition),每个算子有一个或多个**算子子任务**(Operator Subtask)。每个子任务彼此独立,并在不同的线程中运行,或在不同的计算机或容器中运行。
+
+算子子任务数就是其对应算子的**并行度**。在同一程序中,不同算子也可能具有不同的并行度。
+
+
+
+Flink 算子之间可以通过*一对一*(*直传*)模式或*重新分发*模式传输数据:
+
+ - **一对一**模式(例如上图中的 *Source* 和 *map()* 算子之间)可以保留元素的分区和顺序信息。这意味着 *map()* 算子的 subtask[1] 输入的数据以及其顺序与 *Source* 算子的 subtask[1] 输出的数据和顺序完全相同,即同一分区的数据只会进入到下游算子的同一分区。
+
+ - **重新分发**模式(例如上图中的 *map()* 和 *keyBy/window* 之间,以及 *keyBy/window* 和 *Sink* 之间)则会更改数据所在的流分区。当你在程序中选择使用不同的 *transformation*,每个*算子子任务*也会根据不同的 transformation 将数据发送到不同的目标子任务。例如以下这几种 transformation 和其对应分发数据的模式:*keyBy()*(通过散列键重新分区)、*broadcast()*(广播)或 *rebalance()*(随机重新分发)。在*重新分发*数据的过程中,元素只有在每对输出和输入子任务之间才能保留其之间的顺序信息(例如,*keyBy/window* 的 subtask[2] 接收到的 *map()* 的 subtask[1] 中的元素都是有序的)。因此,上图所示的 *keyBy/window* 和 *Sink* 算子之间数据的重新分发时,不同键(key)的聚合结果到达 Sink 的顺序是不确定的。
+
+{% top %}
+
+## 自定义时间流处理
+
+对于大多数流数据处理应用程序而言,能够使用处理实时数据的代码重新处理历史数据并产生确定并一致的结果非常有价值。
+
+在处理流式数据时,我们通常更需要关注事件本身发生的顺序而不是事件被传输以及处理的顺序,因为这能够帮助我们推理出一组事件(事件集合)是何时发生以及结束的。例如电子商务交易或金融交易中涉及到的事件集合。
+
+为了满足上述这类的实时流处理场景,我们通常会使用记录在数据流中的事件时间的时间戳,而不是处理数据的机器时钟的时间戳。
+
+{% top %}
+
+## 有状态流处理
+
+Flink 中的算子可以是有状态的。这意味着如何处理一个事件可能取决于该事件之前所有事件数据的累积结果。Flink 中的状态不仅可以用于简单的场景(例如统计仪表板上每分钟显示的数据),也可以用于复杂的场景(例如训练作弊检测模型)。
+
+Flink 应用程序可以在分布式群集上并行运行,其中每个算子的各个并行实例会在单独的线程中独立运行,并且通常情况下是会在不同的机器上运行。
+
+有状态算子的并行实例组在存储其对应状态时通常是按照键(key)进行分片存储的。每个并行实例算子负责处理一组特定键的事件数据,并且这组键对应的状态会保存在本地。
+
+如下图的 Flink 作业,其前三个算子的并行度为 2,最后一个 sink 算子的并行度为 1,其中第三个算子是有状态的,并且你可以看到第二个算子和第三个算子之间是全互联的(fully-connected),它们之间通过网络进行数据分发。通常情况下,实现这种类型的 Flink 程序是为了通过某些键对数据流进行分区,以便将需要一起处理的事件进行汇合,然后做统一计算处理。
+
+
+
+Flink 应用程序的状态访问都在本地进行,因为这有助于其提高吞吐量和降低延迟。通常情况下 Flink 应用程序都是将状态存储在 JVM 堆上,但如果状态太大,我们也可以选择将其以结构化数据格式存储在高速磁盘中。
+
+
+
+{% top %}
+
+## 通过状态快照实现的容错
+
+通过状态快照和流重放两种方式的组合,Flink 能够提供可容错的,精确一次计算的语义。这些状态快照在执行时会获取并存储分布式 pipeline 中整体的状态,它会将数据源中消费数据的偏移量记录下来,并将整个 job graph 中算子获取到该数据(记录的偏移量对应的数据)时的状态记录并存储下来。当发生故障时,Flink 作业会恢复上次存储的状态,重置数据源从状态中记录的上次消费的偏移量开始重新进行消费处理。而且状态快照在执行时会异步获取状态并存储,并不会阻塞正在进行的数据处理逻辑。
+
+{% top %}
diff --git a/docs/training/streaming_analytics.md b/docs/learn-flink/streaming_analytics.md
similarity index 95%
rename from docs/training/streaming_analytics.md
rename to docs/learn-flink/streaming_analytics.md
index 7dc0503ac48cde..c826ffbee9509f 100644
--- a/docs/training/streaming_analytics.md
+++ b/docs/learn-flink/streaming_analytics.md
@@ -3,7 +3,7 @@ title: Streaming Analytics
nav-id: analytics
nav-pos: 4
nav-title: Streaming Analytics
-nav-parent_id: training
+nav-parent_id: learn-flink
---
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-dependency-plugin
+ 3.1.2
+
+
+ copy-dependencies
+ package
+ copy-dependencies
+ jars
+
+
+
+
+
+
+```
+
+Running `mvn package` in the same directory will create a `jars/` folder containing all the jar files,
+which you can add to the desired folder, Docker image etc.
diff --git a/docs/ops/deployment/index.zh.md b/docs/ops/deployment/index.zh.md
index 4499abb5c086c8..f318ff4a763440 100644
--- a/docs/ops/deployment/index.zh.md
+++ b/docs/ops/deployment/index.zh.md
@@ -29,6 +29,84 @@ When deciding how and where to run Flink, there's a wide range of options availa
* This will be replaced by the TOC
{:toc}
+## Deployment Modes
+
+Flink can execute applications in one of three ways:
+ - in Session Mode,
+ - in a Per-Job Mode, or
+ - in Application Mode.
+
+ The above modes differ in:
+ - the cluster lifecycle and resource isolation guarantees
+ - whether the application's `main()` method is executed on the client or on the cluster.
+
+#### Session Mode
+
+*Session mode* assumes an already running cluster and uses the resources of that cluster to execute any
+submitted application. Applications executed in the same (session) cluster use, and consequently compete
+for, the same resources. This has the advantage that you do not pay the resource overhead of spinning up
+a full cluster for every submitted job. But, if one of the jobs misbehaves or brings down a Task Manager,
+then all jobs running on that Task Manager will be affected by the failure. This, apart from a negative
+impact on the job that caused the failure, implies a potential massive recovery process with all the
+restarting jobs accessing the filesystem concurrently and making it unavailable to other services.
+Additionally, having a single cluster running multiple jobs implies more load for the JobManager, who
+is responsible for the book-keeping of all the jobs in the cluster.
+
+#### Per-Job Mode
+
+Aiming at providing better resource isolation guarantees, the *Per-Job* mode uses the available cluster manager
+framework (e.g. YARN, Kubernetes) to spin up a cluster for each submitted job. This cluster is available to
+that job only. When the job finishes, the cluster is torn down and any lingering resources (files, etc) are
+cleared up. This provides better resource isolation, as a misbehaving job can only bring down its own
+Task Managers. In addition, it spreads the load of book-keeping across multiple JobManagers, as there is
+one per job. For these reasons, the *Per-Job* resource allocation model is the preferred mode by many
+production reasons.
+
+#### Application Mode
+
+In all the above modes, the application's `main()` method is executed on the client side. This process
+includes downloading the application's dependencies locally, executing the `main()` to extract a representation
+of the application that Flink's runtime can understand (i.e. the `JobGraph`) and ship the dependencies and
+the `JobGraph(s)` to the cluster. This makes the Client a heavy resource consumer as it may need substantial
+network bandwidth to download dependencies and ship binaries to the cluster, and CPU cycles to execute the
+`main()`. This problem can be more pronounced when the Client is shared across users.
+
+Building on this observation, the *Application Mode* creates a cluster per submitted application, but this time,
+the `main()` method of the application is executed on the JobManager. Creating a cluster per application can be
+seen as creating a session cluster shared only among the jobs of a particular application, and torn down when
+the application finishes. With this architecture, the *Application Mode* provides the same resource isolation
+and load balancing guarantees as the *Per-Job* mode, but at the granularity of a whole application. Executing
+the `main()` on the JobManager allows for saving the CPU cycles required, but also save the bandwidth required
+for downloading the dependencies locally. Furthermore, it allows for more even spread of the network load of
+downloading the dependencies of the applications in the cluster, as there is one JobManager per application.
+
+
+ Note: In the Application Mode, the `main()` is executed on the cluster and not on the client,
+ as in the other modes. This may have implications for your code as, for example, any paths you register in
+ your environment using the `registerCachedFile()` must be accessible by the JobManager of your application.
+
+
+Compared to the *Per-Job* mode, the *Application Mode* allows the submission of applications consisting of
+multiple jobs. The order of job execution is not affected by the deployment mode but by the call used
+to launch the job. Using `execute()`, which is blocking, establishes an order and it will lead to the
+execution of the "next" job being postponed until "this" job finishes. Using `executeAsync()`, which is
+non-blocking, will lead to the "next" job starting before "this" job finishes.
+
+
+ Attention: The Application Mode allows for multi-`execute()` applications but
+ High-Availability is not supported in these cases. High-Availability in Application Mode is only
+ supported for single-`execute()` applications.
+
+
+#### Summary
+
+In *Session Mode*, the cluster lifecycle is independent of that of any job running on the cluster
+and the resources are shared across all jobs. The *Per-Job* mode pays the price of spinning up a cluster
+for every submitted job, but this comes with better isolation guarantees as the resources are not shared
+across jobs. In this case, the lifecycle of the cluster is bound to that of the job. Finally, the
+*Application Mode* creates a session cluster per application and executes the application's `main()`
+method on the cluster.
+
## Deployment Targets
Apache Flink ships with first class support for a number of common deployment targets.
@@ -41,7 +119,7 @@ Apache Flink ships with first class support for a number of common deployment ta
Run Flink locally for basic testing and experimentation
- Learn more
+ Learn more
@@ -52,7 +130,7 @@ Apache Flink ships with first class support for a number of common deployment ta
A simple solution for running Flink on bare metal or VM's
- Learn more
+ Learn more
@@ -62,8 +140,8 @@ Apache Flink ships with first class support for a number of common deployment ta
Yarn
- Deploy Flink on-top Apache Hadoop's resource manager
- Learn more
+ Deploy Flink on-top of Apache Hadoop's resource manager
+ Learn more
@@ -76,7 +154,7 @@ Apache Flink ships with first class support for a number of common deployment ta
A generic resource manager for running distriubted systems
- Learn more
+ Learn more
@@ -87,7 +165,7 @@ Apache Flink ships with first class support for a number of common deployment ta
A popular solution for running Flink within a containerized environment
- Learn more
+ Learn more
@@ -98,7 +176,7 @@ Apache Flink ships with first class support for a number of common deployment ta
An automated system for deploying containerized applications
- Learn more
+ Learn more
@@ -169,3 +247,58 @@ Supported Environments:
AzureGoogle CloudOn-Premise
+
+## Deployment Best Practices
+
+### How to provide dependencies in the classpath
+
+Flink provides several approaches for providing dependencies (such as `*.jar` files or static data) to Flink or user-provided
+applications. These approaches differ based on the deployment mode and target, but also have commonalities, which are described here.
+
+To provide a dependency, there are the following options:
+- files in the **`lib/` folder** are added to the classpath used to start Flink. It is suitable for libraries such as Hadoop or file systems not available as plugins. Beware that classes added here can potentially interfere with Flink, for example if you are adding a different version of a library already provided by Flink.
+
+- **`plugins//`** are loaded at runtime by Flink through separate classloaders to avoid conflicts with classes loaded and used by Flink. Only jar files which are prepared as [plugins]({% link ops/plugins.zh.md %}) can be added here.
+
+### Download Maven dependencies locally
+
+If you need to extend the Flink with a Maven dependency (and its transitive dependencies),
+you can use an [Apache Maven](https://maven.apache.org) *pom.xml* file to download all required files into a local folder:
+
+*pom.xml*:
+
+```xml
+
+
+ 4.0.0
+ org.apache.flink
+ docker-dependencies
+ 1.0-SNAPSHOT
+
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-dependency-plugin
+ 3.1.2
+
+
+ copy-dependencies
+ package
+ copy-dependencies
+ jars
+
+
+
+
+
+
+```
+
+Running `mvn package` in the same directory will create a `jars/` folder containing all the jar files,
+which you can add to the desired folder, Docker image etc.
diff --git a/docs/ops/deployment/kubernetes.md b/docs/ops/deployment/kubernetes.md
index f79ec67fe65809..6dbb2df1ff5e45 100644
--- a/docs/ops/deployment/kubernetes.md
+++ b/docs/ops/deployment/kubernetes.md
@@ -81,6 +81,10 @@ You can then access the Flink UI via different ways:
{% highlight bash %}./bin/flink run -m : ./examples/streaming/WordCount.jar{% endhighlight %}
+You can also access the queryable state of TaskManager if you create a `NodePort` service for it:
+ 1. Run `kubectl create -f taskmanager-query-state-service.yaml` to create the `NodePort` service on taskmanager. The example of `taskmanager-query-state-service.yaml` can be found in [appendix](#common-cluster-resource-definitions).
+ 2. Run `kubectl get svc flink-taskmanager-query-state` to know the `node-port` of this service. Then you can create the [QueryableStateClient(<public-node-ip>, <node-port>]({% link dev/stream/state/queryable_state.md %}#querying-state) to submit the state queries.
+
In order to terminate the Flink cluster, delete the specific [Session](#deploy-session-cluster) or [Job](#deploy-job-cluster) cluster components
and use `kubectl` to terminate the common components:
@@ -89,6 +93,8 @@ and use `kubectl` to terminate the common components:
kubectl delete -f flink-configuration-configmap.yaml
# if created then also the rest service
kubectl delete -f jobmanager-rest-service.yaml
+ # if created then also the queryable state service
+ kubectl delete -f taskmanager-query-state-service.yaml
```
### Deploy Session Cluster
@@ -99,9 +105,9 @@ Each job needs to be submitted to the cluster after the cluster has been deploye
A *Flink Session cluster* deployment in Kubernetes has at least three components:
-* a *Deployment* which runs a [Flink Master]({{ site.baseurl }}/concepts/glossary.html#flink-master)
+* a *Deployment* which runs a [JobManager]({{ site.baseurl }}/concepts/glossary.html#flink-jobmanager)
* a *Deployment* for a pool of [TaskManagers]({{ site.baseurl }}/concepts/glossary.html#flink-taskmanager)
-* a *Service* exposing the *Flink Master's* REST and UI ports
+* a *Service* exposing the *JobManager's* REST and UI ports
After creating [the common cluster components](#deploy-flink-cluster-on-kubernetes), use [the Session specific resource definitions](#session-cluster-resource-definitions)
to launch the *Session cluster* with the `kubectl` command:
@@ -125,14 +131,14 @@ You can find more details [here](#start-a-job-cluster).
A basic *Flink Job cluster* deployment in Kubernetes has three components:
-* a *Job* which runs a *Flink Master*
+* a *Job* which runs a *JobManager*
* a *Deployment* for a pool of *TaskManagers*
-* a *Service* exposing the *Flink Master's* REST and UI ports
+* a *Service* exposing the *JobManager's* REST and UI ports
Check [the Job cluster specific resource definitions](#job-cluster-resource-definitions) and adjust them accordingly.
The `args` attribute in the `jobmanager-job.yaml` has to specify the main class of the user job.
-See also [how to specify the Flink Master arguments](docker.html#flink-master-additional-command-line-arguments) to understand
+See also [how to specify the JobManager arguments](docker.html#jobmanager-additional-command-line-arguments) to understand
how to pass other `args` to the Flink image in the `jobmanager-job.yaml`.
The *job artifacts* should be available from the `job-artifacts-volume` in [the resource definition examples](#job-cluster-resource-definitions).
@@ -175,13 +181,23 @@ data:
blob.server.port: 6124
jobmanager.rpc.port: 6123
taskmanager.rpc.port: 6122
- queryable-state.server.ports: 6125
+ queryable-state.proxy.ports: 6125
jobmanager.memory.process.size: 1600m
taskmanager.memory.process.size: 1728m
parallelism.default: 2
- log4j.properties: |+
+ log4j-console.properties: |+
+ # This affects logging for both user code and Flink
rootLogger.level = INFO
- rootLogger.appenderRef.file.ref = MainAppender
+ rootLogger.appenderRef.console.ref = ConsoleAppender
+ rootLogger.appenderRef.rolling.ref = RollingFileAppender
+
+ # Uncomment this if you want to _only_ change Flink's logging
+ #logger.flink.name = org.apache.flink
+ #logger.flink.level = INFO
+
+ # The following lines keep the log level of common libraries/connectors on
+ # log level INFO. The root logger does not override this. You have to manually
+ # change the log levels here.
logger.akka.name = akka
logger.akka.level = INFO
logger.kafka.name= org.apache.kafka
@@ -190,14 +206,30 @@ data:
logger.hadoop.level = INFO
logger.zookeeper.name = org.apache.zookeeper
logger.zookeeper.level = INFO
- appender.main.name = MainAppender
- appender.main.type = File
- appender.main.append = false
- appender.main.fileName = ${sys:log.file}
- appender.main.layout.type = PatternLayout
- appender.main.layout.pattern = %d{yyyy-MM-dd HH:mm:ss,SSS} %-5p %-60c %x - %m%n
+
+ # Log all infos to the console
+ appender.console.name = ConsoleAppender
+ appender.console.type = CONSOLE
+ appender.console.layout.type = PatternLayout
+ appender.console.layout.pattern = %d{yyyy-MM-dd HH:mm:ss,SSS} %-5p %-60c %x - %m%n
+
+ # Log all infos in the given rolling file
+ appender.rolling.name = RollingFileAppender
+ appender.rolling.type = RollingFile
+ appender.rolling.append = false
+ appender.rolling.fileName = ${sys:log.file}
+ appender.rolling.filePattern = ${sys:log.file}.%i
+ appender.rolling.layout.type = PatternLayout
+ appender.rolling.layout.pattern = %d{yyyy-MM-dd HH:mm:ss,SSS} %-5p %-60c %x - %m%n
+ appender.rolling.policies.type = Policies
+ appender.rolling.policies.size.type = SizeBasedTriggeringPolicy
+ appender.rolling.policies.size.size=100MB
+ appender.rolling.strategy.type = DefaultRolloverStrategy
+ appender.rolling.strategy.max = 10
+
+ # Suppress the irrelevant (wrong) warnings from the Netty channel handler
logger.netty.name = org.apache.flink.shaded.akka.org.jboss.netty.channel.DefaultChannelPipeline
- logger.netty.level = ERROR
+ logger.netty.level = OFF
{% endhighlight %}
`jobmanager-service.yaml`
@@ -213,8 +245,6 @@ spec:
port: 6123
- name: blob-server
port: 6124
- - name: query-state
- port: 6125
- name: webui
port: 8081
selector:
@@ -234,12 +264,30 @@ spec:
- name: rest
port: 8081
targetPort: 8081
- nodePort: 8081
+ nodePort: 30081
selector:
app: flink
component: jobmanager
{% endhighlight %}
+`taskmanager-query-state-service.yaml`. Optional service, that exposes the TaskManager port to access the queryable state as a public Kubernetes node's port.
+{% highlight yaml %}
+apiVersion: v1
+kind: Service
+metadata:
+ name: flink-taskmanager-query-state
+spec:
+ type: NodePort
+ ports:
+ - name: query-state
+ port: 6125
+ targetPort: 6125
+ nodePort: 30025
+ selector:
+ app: flink
+ component: taskmanager
+{% endhighlight %}
+
### Session cluster resource definitions
`jobmanager-session-deployment.yaml`
@@ -262,15 +310,13 @@ spec:
spec:
containers:
- name: jobmanager
- image: flink:{% if site.is_stable %}{{site.version}}-scala{{site.scala_version_suffix}}{% else %}latest{% endif %}
+ image: flink:{% if site.is_stable %}{{site.version}}-scala{{site.scala_version_suffix}}{% else %}latest # The 'latest' tag contains the latest released version of Flink for a specific Scala version. Do not use the 'latest' tag in production as it will break your setup automatically when a new version is released.{% endif %}
args: ["jobmanager"]
ports:
- containerPort: 6123
name: rpc
- containerPort: 6124
name: blob-server
- - containerPort: 6125
- name: query-state
- containerPort: 8081
name: webui
livenessProbe:
@@ -290,8 +336,8 @@ spec:
items:
- key: flink-conf.yaml
path: flink-conf.yaml
- - key: log4j.properties
- path: log4j.properties
+ - key: log4j-console.properties
+ path: log4j-console.properties
{% endhighlight %}
`taskmanager-session-deployment.yaml`
@@ -314,11 +360,13 @@ spec:
spec:
containers:
- name: taskmanager
- image: flink:{% if site.is_stable %}{{site.version}}-scala{{site.scala_version_suffix}}{% else %}latest{% endif %}
+ image: flink:{% if site.is_stable %}{{site.version}}-scala{{site.scala_version_suffix}}{% else %}latest # The 'latest' tag contains the latest released version of Flink for a specific Scala version. Do not use the 'latest' tag in production as it will break your setup automatically when a new version is released.{% endif %}
args: ["taskmanager"]
ports:
- containerPort: 6122
name: rpc
+ - containerPort: 6125
+ name: query-state
livenessProbe:
tcpSocket:
port: 6122
@@ -336,8 +384,8 @@ spec:
items:
- key: flink-conf.yaml
path: flink-conf.yaml
- - key: log4j.properties
- path: log4j.properties
+ - key: log4j-console.properties
+ path: log4j-console.properties
{% endhighlight %}
### Job cluster resource definitions
@@ -349,11 +397,6 @@ kind: Job
metadata:
name: flink-jobmanager
spec:
- replicas: 1
- selector:
- matchLabels:
- app: flink
- component: jobmanager
template:
metadata:
labels:
@@ -363,16 +406,14 @@ spec:
restartPolicy: OnFailure
containers:
- name: jobmanager
- image: flink:{% if site.is_stable %}{{site.version}}-scala{{site.scala_version_suffix}}{% else %}latest{% endif %}
+ image: flink:{% if site.is_stable %}{{site.version}}-scala{{site.scala_version_suffix}}{% else %}latest # The 'latest' tag contains the latest released version of Flink for a specific Scala version. Do not use the 'latest' tag in production as it will break your setup automatically when a new version is released.{% endif %}
env:
- args: ["standalone-job", "--job-classname", "com.job.ClassName", ["--job-id", "",] ["--fromSavepoint", "/path/to/savepoint", ["--allowNonRestoredState",]] [job arguments]]
+ args: ["standalone-job", "--job-classname", "com.job.ClassName", , ] # optional arguments: ["--job-id", "", "--fromSavepoint", "/path/to/savepoint", "--allowNonRestoredState"]
ports:
- containerPort: 6123
name: rpc
- containerPort: 6124
name: blob-server
- - containerPort: 6125
- name: query-state
- containerPort: 8081
name: webui
livenessProbe:
@@ -394,8 +435,8 @@ spec:
items:
- key: flink-conf.yaml
path: flink-conf.yaml
- - key: log4j.properties
- path: log4j.properties
+ - key: log4j-console.properties
+ path: log4j-console.properties
- name: job-artifacts-volume
hostPath:
path: /host/path/to/job/artifacts
@@ -421,12 +462,14 @@ spec:
spec:
containers:
- name: taskmanager
- image: flink:{% if site.is_stable %}{{site.version}}-scala{{site.scala_version_suffix}}{% else %}latest{% endif %}
+ image: flink:{% if site.is_stable %}{{site.version}}-scala{{site.scala_version_suffix}}{% else %}latest # The 'latest' tag contains the latest released version of Flink for a specific Scala version. Do not use the 'latest' tag in production as it will break your setup automatically when a new version is released.{% endif %}
env:
args: ["taskmanager"]
ports:
- containerPort: 6122
name: rpc
+ - containerPort: 6125
+ name: query-state
livenessProbe:
tcpSocket:
port: 6122
@@ -446,8 +489,8 @@ spec:
items:
- key: flink-conf.yaml
path: flink-conf.yaml
- - key: log4j.properties
- path: log4j.properties
+ - key: log4j-console.properties
+ path: log4j-console.properties
- name: job-artifacts-volume
hostPath:
path: /host/path/to/job/artifacts
diff --git a/docs/ops/deployment/kubernetes.zh.md b/docs/ops/deployment/kubernetes.zh.md
index 804864714e4658..f57135d2fa9d97 100644
--- a/docs/ops/deployment/kubernetes.zh.md
+++ b/docs/ops/deployment/kubernetes.zh.md
@@ -81,6 +81,10 @@ You can then access the Flink UI via different ways:
{% highlight bash %}./bin/flink run -m : ./examples/streaming/WordCount.jar{% endhighlight %}
+You can also access the queryable state of TaskManager if you create a `NodePort` service for it:
+ 1. Run `kubectl create -f taskmanager-query-state-service.yaml` to create the `NodePort` service on taskmanager. The example of `taskmanager-query-state-service.yaml` can be found in [appendix](#common-cluster-resource-definitions).
+ 2. Run `kubectl get svc flink-taskmanager-query-state` to know the `node-port` of this service. Then you can create the [QueryableStateClient(<public-node-ip>, <node-port>]({% link dev/stream/state/queryable_state.zh.md %}#querying-state) to submit the state queries.
+
In order to terminate the Flink cluster, delete the specific [Session](#deploy-session-cluster) or [Job](#deploy-job-cluster) cluster components
and use `kubectl` to terminate the common components:
@@ -89,6 +93,8 @@ and use `kubectl` to terminate the common components:
kubectl delete -f flink-configuration-configmap.yaml
# if created then also the rest service
kubectl delete -f jobmanager-rest-service.yaml
+ # if created then also the queryable state service
+ kubectl delete -f taskmanager-query-state-service.yaml
```
### Deploy Session Cluster
@@ -99,9 +105,9 @@ Each job needs to be submitted to the cluster after the cluster has been deploye
A *Flink Session cluster* deployment in Kubernetes has at least three components:
-* a *Deployment* which runs a [Flink Master]({{ site.baseurl }}/concepts/glossary.html#flink-master)
+* a *Deployment* which runs a [JobManager]({{ site.baseurl }}/concepts/glossary.html#flink-jobmanager)
* a *Deployment* for a pool of [TaskManagers]({{ site.baseurl }}/concepts/glossary.html#flink-taskmanager)
-* a *Service* exposing the *Flink Master's* REST and UI ports
+* a *Service* exposing the *JobManager's* REST and UI ports
After creating [the common cluster components](#deploy-flink-cluster-on-kubernetes), use [the Session specific resource definitions](#session-cluster-resource-definitions)
to launch the *Session cluster* with the `kubectl` command:
@@ -125,14 +131,14 @@ You can find more details [here](#start-a-job-cluster).
A basic *Flink Job cluster* deployment in Kubernetes has three components:
-* a *Job* which runs a *Flink Master*
+* a *Job* which runs a *JobManager*
* a *Deployment* for a pool of *TaskManagers*
-* a *Service* exposing the *Flink Master's* REST and UI ports
+* a *Service* exposing the *JobManager's* REST and UI ports
Check [the Job cluster specific resource definitions](#job-cluster-resource-definitions) and adjust them accordingly.
The `args` attribute in the `jobmanager-job.yaml` has to specify the main class of the user job.
-See also [how to specify the Flink Master arguments](docker.html#flink-master-additional-command-line-arguments) to understand
+See also [how to specify the JobManager arguments](docker.html#jobmanager-additional-command-line-arguments) to understand
how to pass other `args` to the Flink image in the `jobmanager-job.yaml`.
The *job artifacts* should be available from the `job-artifacts-volume` in [the resource definition examples](#job-cluster-resource-definitions).
@@ -175,13 +181,23 @@ data:
blob.server.port: 6124
jobmanager.rpc.port: 6123
taskmanager.rpc.port: 6122
- queryable-state.server.ports: 6125
+ queryable-state.proxy.ports: 6125
jobmanager.memory.process.size: 1600m
taskmanager.memory.process.size: 1728m
parallelism.default: 2
- log4j.properties: |+
+ log4j-console.properties: |+
+ # This affects logging for both user code and Flink
rootLogger.level = INFO
- rootLogger.appenderRef.file.ref = MainAppender
+ rootLogger.appenderRef.console.ref = ConsoleAppender
+ rootLogger.appenderRef.rolling.ref = RollingFileAppender
+
+ # Uncomment this if you want to _only_ change Flink's logging
+ #logger.flink.name = org.apache.flink
+ #logger.flink.level = INFO
+
+ # The following lines keep the log level of common libraries/connectors on
+ # log level INFO. The root logger does not override this. You have to manually
+ # change the log levels here.
logger.akka.name = akka
logger.akka.level = INFO
logger.kafka.name= org.apache.kafka
@@ -190,14 +206,30 @@ data:
logger.hadoop.level = INFO
logger.zookeeper.name = org.apache.zookeeper
logger.zookeeper.level = INFO
- appender.main.name = MainAppender
- appender.main.type = File
- appender.main.append = false
- appender.main.fileName = ${sys:log.file}
- appender.main.layout.type = PatternLayout
- appender.main.layout.pattern = %d{yyyy-MM-dd HH:mm:ss,SSS} %-5p %-60c %x - %m%n
+
+ # Log all infos to the console
+ appender.console.name = ConsoleAppender
+ appender.console.type = CONSOLE
+ appender.console.layout.type = PatternLayout
+ appender.console.layout.pattern = %d{yyyy-MM-dd HH:mm:ss,SSS} %-5p %-60c %x - %m%n
+
+ # Log all infos in the given rolling file
+ appender.rolling.name = RollingFileAppender
+ appender.rolling.type = RollingFile
+ appender.rolling.append = false
+ appender.rolling.fileName = ${sys:log.file}
+ appender.rolling.filePattern = ${sys:log.file}.%i
+ appender.rolling.layout.type = PatternLayout
+ appender.rolling.layout.pattern = %d{yyyy-MM-dd HH:mm:ss,SSS} %-5p %-60c %x - %m%n
+ appender.rolling.policies.type = Policies
+ appender.rolling.policies.size.type = SizeBasedTriggeringPolicy
+ appender.rolling.policies.size.size=100MB
+ appender.rolling.strategy.type = DefaultRolloverStrategy
+ appender.rolling.strategy.max = 10
+
+ # Suppress the irrelevant (wrong) warnings from the Netty channel handler
logger.netty.name = org.apache.flink.shaded.akka.org.jboss.netty.channel.DefaultChannelPipeline
- logger.netty.level = ERROR
+ logger.netty.level = OFF
{% endhighlight %}
`jobmanager-service.yaml`
@@ -213,8 +245,6 @@ spec:
port: 6123
- name: blob-server
port: 6124
- - name: query-state
- port: 6125
- name: webui
port: 8081
selector:
@@ -234,12 +264,30 @@ spec:
- name: rest
port: 8081
targetPort: 8081
- nodePort: 8081
+ nodePort: 30081
selector:
app: flink
component: jobmanager
{% endhighlight %}
+`taskmanager-query-state-service.yaml`. Optional service, that exposes the TaskManager port to access the queryable state as a public Kubernetes node's port.
+{% highlight yaml %}
+apiVersion: v1
+kind: Service
+metadata:
+ name: flink-taskmanager-query-state
+spec:
+ type: NodePort
+ ports:
+ - name: query-state
+ port: 6125
+ targetPort: 6125
+ nodePort: 30025
+ selector:
+ app: flink
+ component: taskmanager
+{% endhighlight %}
+
### Session cluster resource definitions
`jobmanager-session-deployment.yaml`
@@ -262,15 +310,13 @@ spec:
spec:
containers:
- name: jobmanager
- image: flink:{% if site.is_stable %}{{site.version}}-scala{{site.scala_version_suffix}}{% else %}latest{% endif %}
+ image: flink:{% if site.is_stable %}{{site.version}}-scala{{site.scala_version_suffix}}{% else %}latest # The 'latest' tag contains the latest released version of Flink for a specific Scala version. Do not use the 'latest' tag in production as it will break your setup automatically when a new version is released.{% endif %}
args: ["jobmanager"]
ports:
- containerPort: 6123
name: rpc
- containerPort: 6124
name: blob-server
- - containerPort: 6125
- name: query-state
- containerPort: 8081
name: webui
livenessProbe:
@@ -290,8 +336,8 @@ spec:
items:
- key: flink-conf.yaml
path: flink-conf.yaml
- - key: log4j.properties
- path: log4j.properties
+ - key: log4j-console.properties
+ path: log4j-console.properties
{% endhighlight %}
`taskmanager-session-deployment.yaml`
@@ -314,11 +360,13 @@ spec:
spec:
containers:
- name: taskmanager
- image: flink:{% if site.is_stable %}{{site.version}}-scala{{site.scala_version_suffix}}{% else %}latest{% endif %}
+ image: flink:{% if site.is_stable %}{{site.version}}-scala{{site.scala_version_suffix}}{% else %}latest # The 'latest' tag contains the latest released version of Flink for a specific Scala version. Do not use the 'latest' tag in production as it will break your setup automatically when a new version is released.{% endif %}
args: ["taskmanager"]
ports:
- containerPort: 6122
name: rpc
+ - containerPort: 6125
+ name: query-state
livenessProbe:
tcpSocket:
port: 6122
@@ -336,8 +384,8 @@ spec:
items:
- key: flink-conf.yaml
path: flink-conf.yaml
- - key: log4j.properties
- path: log4j.properties
+ - key: log4j-console.properties
+ path: log4j-console.properties
{% endhighlight %}
### Job cluster resource definitions
@@ -349,11 +397,6 @@ kind: Job
metadata:
name: flink-jobmanager
spec:
- replicas: 1
- selector:
- matchLabels:
- app: flink
- component: jobmanager
template:
metadata:
labels:
@@ -363,16 +406,14 @@ spec:
restartPolicy: OnFailure
containers:
- name: jobmanager
- image: flink:{% if site.is_stable %}{{site.version}}-scala{{site.scala_version_suffix}}{% else %}latest{% endif %}
+ image: flink:{% if site.is_stable %}{{site.version}}-scala{{site.scala_version_suffix}}{% else %}latest # The 'latest' tag contains the latest released version of Flink for a specific Scala version. Do not use the 'latest' tag in production as it will break your setup automatically when a new version is released.{% endif %}
env:
- args: ["standalone-job", "--job-classname", "com.job.ClassName", ["--job-id", "",] ["--fromSavepoint", "/path/to/savepoint", ["--allowNonRestoredState",]] [job arguments]]
+ args: ["standalone-job", "--job-classname", "com.job.ClassName", , ] # optional arguments: ["--job-id", "", "--fromSavepoint", "/path/to/savepoint", "--allowNonRestoredState"]
ports:
- containerPort: 6123
name: rpc
- containerPort: 6124
name: blob-server
- - containerPort: 6125
- name: query-state
- containerPort: 8081
name: webui
livenessProbe:
@@ -394,8 +435,8 @@ spec:
items:
- key: flink-conf.yaml
path: flink-conf.yaml
- - key: log4j.properties
- path: log4j.properties
+ - key: log4j-console.properties
+ path: log4j-console.properties
- name: job-artifacts-volume
hostPath:
path: /host/path/to/job/artifacts
@@ -421,12 +462,14 @@ spec:
spec:
containers:
- name: taskmanager
- image: flink:{% if site.is_stable %}{{site.version}}-scala{{site.scala_version_suffix}}{% else %}latest{% endif %}
+ image: flink:{% if site.is_stable %}{{site.version}}-scala{{site.scala_version_suffix}}{% else %}latest # The 'latest' tag contains the latest released version of Flink for a specific Scala version. Do not use the 'latest' tag in production as it will break your setup automatically when a new version is released.{% endif %}
env:
args: ["taskmanager"]
ports:
- containerPort: 6122
name: rpc
+ - containerPort: 6125
+ name: query-state
livenessProbe:
tcpSocket:
port: 6122
@@ -446,8 +489,8 @@ spec:
items:
- key: flink-conf.yaml
path: flink-conf.yaml
- - key: log4j.properties
- path: log4j.properties
+ - key: log4j-console.properties
+ path: log4j-console.properties
- name: job-artifacts-volume
hostPath:
path: /host/path/to/job/artifacts
diff --git a/docs/ops/deployment/mesos.md b/docs/ops/deployment/mesos.md
index a273171660fda0..6e4515ace03bf1 100644
--- a/docs/ops/deployment/mesos.md
+++ b/docs/ops/deployment/mesos.md
@@ -224,7 +224,7 @@ For example:
### High Availability
-You will need to run a service like Marathon or Apache Aurora which takes care of restarting the Flink master process in case of node or process failures.
+You will need to run a service like Marathon or Apache Aurora which takes care of restarting the JobManager process in case of node or process failures.
In addition, Zookeeper needs to be configured like described in the [High Availability section of the Flink docs]({{ site.baseurl }}/ops/jobmanager_high_availability.html).
#### Marathon
diff --git a/docs/ops/deployment/mesos.zh.md b/docs/ops/deployment/mesos.zh.md
index 2d082357634df2..df56c958305b6d 100644
--- a/docs/ops/deployment/mesos.zh.md
+++ b/docs/ops/deployment/mesos.zh.md
@@ -224,7 +224,7 @@ For example:
### High Availability
-You will need to run a service like Marathon or Apache Aurora which takes care of restarting the Flink master process in case of node or process failures.
+You will need to run a service like Marathon or Apache Aurora which takes care of restarting the JobManager process in case of node or process failures.
In addition, Zookeeper needs to be configured like described in the [High Availability section of the Flink docs]({{ site.baseurl }}/ops/jobmanager_high_availability.html).
#### Marathon
diff --git a/docs/ops/deployment/native_kubernetes.md b/docs/ops/deployment/native_kubernetes.md
index 00f783cebdff4f..ec4179064528dc 100644
--- a/docs/ops/deployment/native_kubernetes.md
+++ b/docs/ops/deployment/native_kubernetes.md
@@ -30,7 +30,7 @@ This page describes how to deploy a Flink session cluster natively on [Kubernete
{:toc}
-Flink's native Kubernetes integration is still experimental. There may be changes in the configuration and CLI flags in latter versions. Job clusters are not yet supported.
+Flink's native Kubernetes integration is still experimental. There may be changes in the configuration and CLI flags in latter versions.
## Requirements
@@ -63,7 +63,7 @@ Although this setting may cause more cloud cost it has the effect that starting
faster and during development you have more time to inspect the logfiles of your job.
{% highlight bash %}
-./bin/kubernetes-session.sh \
+$ ./bin/kubernetes-session.sh \
-Dkubernetes.cluster-id= \
-Dtaskmanager.memory.process.size=4096m \
-Dkubernetes.taskmanager.cpu=2 \
@@ -83,13 +83,13 @@ If you want to use a custom Docker image to deploy Flink containers, check [the
If you created a custom Docker image you can provide it by setting the [`kubernetes.container.image`](../config.html#kubernetes-container-image) configuration option:
{% highlight bash %}
-./bin/kubernetes-session.sh \
+$ ./bin/kubernetes-session.sh \
-Dkubernetes.cluster-id= \
-Dtaskmanager.memory.process.size=4096m \
-Dkubernetes.taskmanager.cpu=2 \
-Dtaskmanager.numberOfTaskSlots=4 \
-Dresourcemanager.taskmanager-timeout=3600000 \
- -Dkubernetes.container.image=
+ -Dkubernetes.container.image=
{% endhighlight %}
### Submitting jobs to an existing Session
@@ -97,13 +97,13 @@ If you created a custom Docker image you can provide it by setting the [`kuberne
Use the following command to submit a Flink Job to the Kubernetes cluster.
{% highlight bash %}
-$ ./bin/flink run -d -e kubernetes-session -Dkubernetes.cluster-id= examples/streaming/WindowJoin.jar
+$ ./bin/flink run -d -t kubernetes-session -Dkubernetes.cluster-id= examples/streaming/WindowJoin.jar
{% endhighlight %}
### Accessing Job Manager UI
There are several ways to expose a Service onto an external (outside of your cluster) IP address.
-This can be configured using `kubernetes.service.exposed.type`.
+This can be configured using [`kubernetes.rest-service.exposed.type`]({% link ops/config.md %}#kubernetes-rest-service-exposed-type).
- `ClusterIP`: Exposes the service on a cluster-internal IP.
The Service is only reachable within the cluster. If you want to access the Job Manager ui or submit job to the existing session, you need to start a local proxy.
@@ -116,10 +116,12 @@ $ kubectl port-forward service/ 8081
- `NodePort`: Exposes the service on each Node’s IP at a static port (the `NodePort`). `:` could be used to contact the Job Manager Service. `NodeIP` could be easily replaced with Kubernetes ApiServer address.
You could find it in your kube config file.
-- `LoadBalancer`: Default value, exposes the service externally using a cloud provider’s load balancer.
+- `LoadBalancer`: Exposes the service externally using a cloud provider’s load balancer.
Since the cloud provider and Kubernetes needs some time to prepare the load balancer, you may get a `NodePort` JobManager Web Interface in the client log.
You can use `kubectl get services/` to get EXTERNAL-IP and then construct the load balancer JobManager Web Interface manually `http://:8081`.
+ Warning! Your JobManager (which can run arbitary jar files) might be exposed to the public internet, without authentication.
+
- `ExternalName`: Map a service to a DNS name, not supported in current version.
Please reference the official documentation on [publishing services in Kubernetes](https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types) for more information.
@@ -170,6 +172,42 @@ appender.console.layout.pattern = %d{yyyy-MM-dd HH:mm:ss,SSS} %-5p %-60c %x - %m
If the pod is running, you can use `kubectl exec -it bash` to tunnel in and view the logs or debug the process.
+## Flink Kubernetes Application
+
+### Start Flink Application
+
+Application mode allows users to create a single image containing their Job and the Flink runtime, which will automatically create and destroy cluster components as needed. The Flink community provides base docker images [customized](docker.html#customize-flink-image) for any use case.
+
+{% highlight dockerfile %}
+FROM flink
+RUN mkdir -p $FLINK_HOME/usrlib
+COPY /path/of/my-flink-job-*.jar $FLINK_HOME/usrlib/my-flink-job.jar
+{% endhighlight %}
+
+Use the following command to start a Flink application.
+{% highlight bash %}
+$ ./bin/flink run-application -p 8 -t kubernetes-application \
+ -Dkubernetes.cluster-id= \
+ -Dtaskmanager.memory.process.size=4096m \
+ -Dkubernetes.taskmanager.cpu=2 \
+ -Dtaskmanager.numberOfTaskSlots=4 \
+ -Dkubernetes.container.image= \
+ local:///opt/flink/usrlib/my-flink-job.jar
+{% endhighlight %}
+
+Note: Only "local" is supported as schema for application mode. This assumes that the jar is located in the image, not the Flink client.
+
+Note: All the jars in the "$FLINK_HOME/usrlib" directory in the image will be added to user classpath.
+
+### Stop Flink Application
+
+When an application is stopped, all Flink cluster resources are automatically destroyed.
+As always, Jobs may stop when manually canceled or, in the case of bounded Jobs, complete.
+
+{% highlight bash %}
+$ ./bin/flink cancel -t kubernetes-application -Dkubernetes.cluster-id=
+{% endhighlight %}
+
## Kubernetes concepts
### Namespaces
@@ -184,7 +222,7 @@ It can limit the quantity of objects that can be created in a namespace by type,
### RBAC
Role-based access control ([RBAC](https://kubernetes.io/docs/reference/access-authn-authz/rbac/)) is a method of regulating access to compute or network resources based on the roles of individual users within an enterprise.
-Users can configure RBAC roles and service accounts used by Flink JobManager to access the Kubernetes API server within the Kubernetes cluster.
+Users can configure RBAC roles and service accounts used by JobManager to access the Kubernetes API server within the Kubernetes cluster.
Every namespace has a default service account, however, the `default` service account may not have the permission to create or delete pods within the Kubernetes cluster.
Users may need to update the permission of `default` service account or specify another service account that has the right role bound.
@@ -210,13 +248,12 @@ This section briefly explains how Flink and Kubernetes interact.
When creating a Flink Kubernetes session cluster, the Flink client will first connect to the Kubernetes ApiServer to submit the cluster description, including ConfigMap spec, Job Manager Service spec, Job Manager Deployment spec and Owner Reference.
-Kubernetes will then create the Flink master deployment, during which time the Kubelet will pull the image, prepare and mount the volume, and then execute the start command.
-After the master pod has launched, the Dispatcher and KubernetesResourceManager are available and the cluster is ready to accept one or more jobs.
+Kubernetes will then create the JobManager deployment, during which time the Kubelet will pull the image, prepare and mount the volume, and then execute the start command.
+After the JobManager pod has launched, the Dispatcher and KubernetesResourceManager are available and the cluster is ready to accept one or more jobs.
When users submit jobs through the Flink client, the job graph will be generated by the client and uploaded along with users jars to the Dispatcher.
-A JobMaster for that Job will be then be spawned.
-The JobMaster requests resources, known as slots, from the KubernetesResourceManager.
+The JobManager requests resources, known as slots, from the KubernetesResourceManager.
If no slots are available, the resource manager will bring up TaskManager pods and registering them with the cluster.
{% top %}
diff --git a/docs/ops/deployment/native_kubernetes.zh.md b/docs/ops/deployment/native_kubernetes.zh.md
index e341154af2d8da..6d1c0ae0112e12 100644
--- a/docs/ops/deployment/native_kubernetes.zh.md
+++ b/docs/ops/deployment/native_kubernetes.zh.md
@@ -24,46 +24,44 @@ specific language governing permissions and limitations
under the License.
-->
-This page describes how to deploy a Flink session cluster natively on [Kubernetes](https://kubernetes.io).
+本页面描述了如何在 [Kubernetes](https://kubernetes.io) 原生地部署 Flink session 集群。
* This will be replaced by the TOC
{:toc}
-Flink's native Kubernetes integration is still experimental. There may be changes in the configuration and CLI flags in latter versions. Job clusters are not yet supported.
+Flink 的原生 Kubernetes 集成仍处于试验阶段。在以后的版本中,配置和 CLI flags 可能会发生变化。
-## Requirements
+## 要求
-- Kubernetes 1.9 or above.
-- KubeConfig, which has access to list, create, delete pods and services, configurable via `~/.kube/config`. You can verify permissions by running `kubectl auth can-i pods`.
-- Kubernetes DNS enabled.
-- A service Account with [RBAC](#rbac) permissions to create, delete pods.
+- Kubernetes 版本 1.9 或以上。
+- KubeConfig 可以查看、创建、删除 pods 和 services,可以通过`~/.kube/config` 配置。你可以通过运行 `kubectl auth can-i pods` 来验证权限。
+- 启用 Kubernetes DNS。
+- 具有 [RBAC](#rbac) 权限的 Service Account 可以创建、删除 pods。
## Flink Kubernetes Session
-### Start Flink Session
+### 启动 Flink Session
-Follow these instructions to start a Flink Session within your Kubernetes cluster.
+按照以下说明在 Kubernetes 集群中启动 Flink Session。
-A session will start all required Flink services (JobManager and TaskManagers) so that you can submit programs to the cluster.
-Note that you can run multiple programs per session.
+Session 集群将启动所有必需的 Flink 服务(JobManager 和 TaskManagers),以便你可以将程序提交到集群。
+注意你可以在每个 session 上运行多个程序。
{% highlight bash %}
$ ./bin/kubernetes-session.sh
{% endhighlight %}
-All the Kubernetes configuration options can be found in our [configuration guide]({{ site.baseurl }}/zh/ops/config.html#kubernetes).
+所有 Kubernetes 配置项都可以在我们的[配置指南]({{ site.baseurl }}/zh/ops/config.html#kubernetes)中找到。
-**Example**: Issue the following command to start a session cluster with 4 GB of memory and 2 CPUs with 4 slots per TaskManager:
+**示例**: 执行以下命令启动 session 集群,每个 TaskManager 分配 4 GB 内存、2 CPUs、4 slots:
-In this example we override the `resourcemanager.taskmanager-timeout` setting to make
-the pods with task managers remain for a longer period than the default of 30 seconds.
-Although this setting may cause more cloud cost it has the effect that starting new jobs is in some scenarios
-faster and during development you have more time to inspect the logfiles of your job.
+在此示例中,我们覆盖了 `resourcemanager.taskmanager-timeout` 配置,为了使运行 taskmanager 的 pod 停留时间比默认的 30 秒更长。
+尽管此设置可能在云环境下增加成本,但在某些情况下能够更快地启动新作业,并且在开发过程中,你有更多的时间检查作业的日志文件。
{% highlight bash %}
-./bin/kubernetes-session.sh \
+$ ./bin/kubernetes-session.sh \
-Dkubernetes.cluster-id= \
-Dtaskmanager.memory.process.size=4096m \
-Dkubernetes.taskmanager.cpu=2 \
@@ -71,94 +69,95 @@ faster and during development you have more time to inspect the logfiles of your
-Dresourcemanager.taskmanager-timeout=3600000
{% endhighlight %}
-The system will use the configuration in `conf/flink-conf.yaml`.
-Please follow our [configuration guide]({{ site.baseurl }}/zh/ops/config.html) if you want to change something.
+系统将使用 `conf/flink-conf.yaml` 中的配置。
+如果你更改某些配置,请遵循我们的[配置指南]({{ site.baseurl }}/zh/ops/config.html)。
-If you do not specify a particular name for your session by `kubernetes.cluster-id`, the Flink client will generate a UUID name.
+如果你未通过 `kubernetes.cluster-id` 为 session 指定特定名称,Flink 客户端将会生成一个 UUID 名称。
-### Custom Flink Docker image
+### 自定义 Flink Docker 镜像
-If you want to use a custom Docker image to deploy Flink containers, check [the Flink Docker image documentation](docker.html),
-[its tags](docker.html#image-tags), [how to customize the Flink Docker image](docker.html#customize-flink-image) and [enable plugins](docker.html#using-plugins).
-If you created a custom Docker image you can provide it by setting the [`kubernetes.container.image`](../config.html#kubernetes-container-image) configuration option:
+如果要使用自定义的 Docker 镜像部署 Flink 容器,请查看 [Flink Docker 镜像文档](docker.html)、[镜像 tags](docker.html#image-tags)、[如何自定义 Flink Docker 镜像](docker.html#customize-flink-image)和[启用插件](docker.html#using-plugins)。
+如果创建了自定义的 Docker 镜像,则可以通过设置 [`kubernetes.container.image`](../config.html#kubernetes-container-image) 配置项来指定它:
{% highlight bash %}
-./bin/kubernetes-session.sh \
+$ ./bin/kubernetes-session.sh \
-Dkubernetes.cluster-id= \
-Dtaskmanager.memory.process.size=4096m \
-Dkubernetes.taskmanager.cpu=2 \
-Dtaskmanager.numberOfTaskSlots=4 \
-Dresourcemanager.taskmanager-timeout=3600000 \
- -Dkubernetes.container.image=
+ -Dkubernetes.container.image=
{% endhighlight %}
-### Submitting jobs to an existing Session
+### 将作业提交到现有 Session
-Use the following command to submit a Flink Job to the Kubernetes cluster.
+使用以下命令将 Flink 作业提交到 Kubernetes 集群。
{% highlight bash %}
-$ ./bin/flink run -d -e kubernetes-session -Dkubernetes.cluster-id= examples/streaming/WindowJoin.jar
+$ ./bin/flink run -d -t kubernetes-session -Dkubernetes.cluster-id= examples/streaming/WindowJoin.jar
{% endhighlight %}
-### Accessing Job Manager UI
+### 访问 Job Manager UI
-There are several ways to expose a Service onto an external (outside of your cluster) IP address.
-This can be configured using `kubernetes.service.exposed.type`.
+有几种方法可以将服务暴露到外部(集群外部) IP 地址。
+可以使用 [`kubernetes.rest-service.exposed.type`]({% link ops/config.zh.md %}#kubernetes-rest-service-exposed-type) 进行配置。
-- `ClusterIP`: Exposes the service on a cluster-internal IP.
-The Service is only reachable within the cluster. If you want to access the Job Manager ui or submit job to the existing session, you need to start a local proxy.
-You can then use `localhost:8081` to submit a Flink job to the session or view the dashboard.
+- `ClusterIP`:通过集群内部 IP 暴露服务。
+该服务只能在集群中访问。如果想访问 JobManager ui 或将作业提交到现有 session,则需要启动一个本地代理。
+然后你可以使用 `localhost:8081` 将 Flink 作业提交到 session 或查看仪表盘。
{% highlight bash %}
$ kubectl port-forward service/ 8081
{% endhighlight %}
-- `NodePort`: Exposes the service on each Node’s IP at a static port (the `NodePort`). `:` could be used to contact the Job Manager Service. `NodeIP` could be easily replaced with Kubernetes ApiServer address.
-You could find it in your kube config file.
+- `NodePort`:通过每个 Node 上的 IP 和静态端口(`NodePort`)暴露服务。`:` 可以用来连接 JobManager 服务。`NodeIP` 可以很容易地用 Kubernetes ApiServer 地址替换。
+你可以在 kube 配置文件找到它。
-- `LoadBalancer`: Default value, exposes the service externally using a cloud provider’s load balancer.
-Since the cloud provider and Kubernetes needs some time to prepare the load balancer, you may get a `NodePort` JobManager Web Interface in the client log.
-You can use `kubectl get services/` to get EXTERNAL-IP and then construct the load balancer JobManager Web Interface manually `http://:8081`.
+- `LoadBalancer`:使用云提供商的负载均衡器在外部暴露服务。
+由于云提供商和 Kubernetes 需要一些时间来准备负载均衡器,因为你可能在客户端日志中获得一个 `NodePort` 的 JobManager Web 界面。
+你可以使用 `kubectl get services/` 获取 EXTERNAL-IP 然后手动构建负载均衡器 JobManager Web 界面 `http://:8081`。
-- `ExternalName`: Map a service to a DNS name, not supported in current version.
+ 警告! JobManager 可能会在无需认证的情况下暴露在公网上,同时可以提交任务运行。
-Please reference the official documentation on [publishing services in Kubernetes](https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types) for more information.
+- `ExternalName`:将服务映射到 DNS 名称,当前版本不支持。
-### Attach to an existing Session
+有关更多信息,请参考官方文档[在 Kubernetes 上发布服务](https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types)。
-The Kubernetes session is started in detached mode by default, meaning the Flink client will exit after submitting all the resources to the Kubernetes cluster. Use the following command to attach to an existing session.
+### 连接现有 Session
+
+默认情况下,Kubernetes session 以后台模式启动,这意味着 Flink 客户端在将所有资源提交到 Kubernetes 集群后会退出。使用以下命令来连接现有 session。
{% highlight bash %}
$ ./bin/kubernetes-session.sh -Dkubernetes.cluster-id= -Dexecution.attached=true
{% endhighlight %}
-### Stop Flink Session
+### 停止 Flink Session
-To stop a Flink Kubernetes session, attach the Flink client to the cluster and type `stop`.
+要停止 Flink Kubernetes session,将 Flink 客户端连接到集群并键入 `stop`。
{% highlight bash %}
$ echo 'stop' | ./bin/kubernetes-session.sh -Dkubernetes.cluster-id= -Dexecution.attached=true
{% endhighlight %}
-#### Manual Resource Cleanup
+#### 手动清理资源
-Flink uses [Kubernetes OwnerReference's](https://kubernetes.io/docs/concepts/workloads/controllers/garbage-collection/) to cleanup all cluster components.
-All the Flink created resources, including `ConfigMap`, `Service`, `Pod`, have been set the OwnerReference to `deployment/`.
-When the deployment is deleted, all other resources will be deleted automatically.
+Flink 用 [Kubernetes OwnerReference's](https://kubernetes.io/docs/concepts/workloads/controllers/garbage-collection/) 来清理所有集群组件。
+所有 Flink 创建的资源,包括 `ConfigMap`、`Service`、`Pod`,已经将 OwnerReference 设置为 `deployment/`。
+删除 deployment 后,所有其他资源将自动删除。
{% highlight bash %}
$ kubectl delete deployment/
{% endhighlight %}
-## Log Files
+## 日志文件
-By default, the JobManager and TaskManager only store logs under `/opt/flink/log` in each pod.
-If you want to use `kubectl logs ` to view the logs, you must perform the following:
+默认情况下,JobManager 和 TaskManager 只把日志存储在每个 pod 中的 `/opt/flink/log` 下。
+如果要使用 `kubectl logs ` 查看日志,必须执行以下操作:
-1. Add a new appender to the log4j.properties in the Flink client.
-2. Add the following 'appenderRef' the rootLogger in log4j.properties `rootLogger.appenderRef.console.ref = ConsoleAppender`.
-3. Remove the redirect args by adding config option `-Dkubernetes.container-start-command-template="%java% %classpath% %jvmmem% %jvmopts% %logging% %class% %args%"`.
-4. Stop and start your session again. Now you could use `kubectl logs` to view your logs.
+1. 在 Flink 客户端的 log4j.properties 中增加新的 appender。
+2. 在 log4j.properties 的 rootLogger 中增加如下 'appenderRef',`rootLogger.appenderRef.console.ref = ConsoleAppender`。
+3. 通过增加配置项 `-Dkubernetes.container-start-command-template="%java% %classpath% %jvmmem% %jvmopts% %logging% %class% %args%"` 来删除重定向的参数。
+4. 停止并重启你的 session。现在你可以使用 `kubectl logs` 查看日志了。
{% highlight bash %}
# Log all infos to the console
@@ -168,55 +167,92 @@ appender.console.layout.type = PatternLayout
appender.console.layout.pattern = %d{yyyy-MM-dd HH:mm:ss,SSS} %-5p %-60c %x - %m%n
{% endhighlight %}
-If the pod is running, you can use `kubectl exec -it bash` to tunnel in and view the logs or debug the process.
+如果 pod 正在运行,可以使用 `kubectl exec -it bash` 进入 pod 并查看日志或调试进程。
+
+## Flink Kubernetes Application
+
+### 启动 Flink Application
+
+Application 模式允许用户创建单个镜像,其中包含他们的作业和 Flink 运行时,该镜像将按需自动创建和销毁集群组件。Flink 社区提供了可以构建[多用途自定义镜像](docker.html#customize-flink-image)的基础镜像。
+
+{% highlight dockerfile %}
+FROM flink
+RUN mkdir -p $FLINK_HOME/usrlib
+COPY /path/of/my-flink-job-*.jar $FLINK_HOME/usrlib/my-flink-job.jar
+{% endhighlight %}
+
+使用以下命令启动 Flink Application。
+{% highlight bash %}
+$ ./bin/flink run-application -p 8 -t kubernetes-application \
+ -Dkubernetes.cluster-id= \
+ -Dtaskmanager.memory.process.size=4096m \
+ -Dkubernetes.taskmanager.cpu=2 \
+ -Dtaskmanager.numberOfTaskSlots=4 \
+ -Dkubernetes.container.image= \
+ local:///opt/flink/usrlib/my-flink-job.jar
+{% endhighlight %}
+
+注意:Application 模式只支持 "local" 作为 schema。默认 jar 位于镜像中,而不是 Flink 客户端中。
+
+注意:镜像的 "$FLINK_HOME/usrlib" 目录下的所有 jar 将会被加到用户 classpath 中。
+
+### 停止 Flink Application
+
+当 Application 停止时,所有 Flink 集群资源都会自动销毁。
+与往常一样,作业可能会在手动取消或执行完的情况下停止。
+
+{% highlight bash %}
+$ ./bin/flink cancel -t kubernetes-application -Dkubernetes.cluster-id=
+{% endhighlight %}
-## Kubernetes concepts
+## Kubernetes 概念
-### Namespaces
+### 命名空间
-[Namespaces in Kubernetes](https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/) are a way to divide cluster resources between multiple users (via resource quota).
-It is similar to the queue concept in Yarn cluster. Flink on Kubernetes can use namespaces to launch Flink clusters.
-The namespace can be specified using the `-Dkubernetes.namespace=default` argument when starting a Flink cluster.
+[Kubernetes 中的命名空间](https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/)是一种在多个用户之间划分集群资源的方法(通过资源配额)。
+它类似于 Yarn 集群中的队列概念。Flink on Kubernetes 可以使用命名空间来启动 Flink 集群。
+启动 Flink 集群时,可以使用 `-Dkubernetes.namespace=default` 参数来指定命名空间。
-[ResourceQuota](https://kubernetes.io/docs/concepts/policy/resource-quotas/) provides constraints that limit aggregate resource consumption per namespace.
-It can limit the quantity of objects that can be created in a namespace by type, as well as the total amount of compute resources that may be consumed by resources in that project.
+[资源配额](https://kubernetes.io/docs/concepts/policy/resource-quotas/)提供了限制每个命名空间的合计资源消耗的约束。
+它可以按类型限制可在命名空间中创建的对象数量,以及该项目中的资源可能消耗的计算资源总量。
-### RBAC
+
+### 基于角色的访问控制
-Role-based access control ([RBAC](https://kubernetes.io/docs/reference/access-authn-authz/rbac/)) is a method of regulating access to compute or network resources based on the roles of individual users within an enterprise.
-Users can configure RBAC roles and service accounts used by Flink JobManager to access the Kubernetes API server within the Kubernetes cluster.
+基于角色的访问控制([RBAC](https://kubernetes.io/docs/reference/access-authn-authz/rbac/))是一种在企业内部基于单个用户的角色来调节对计算或网络资源的访问的方法。
+用户可以配置 RBAC 角色和服务账户,JobManager 使用这些角色和服务帐户访问 Kubernetes 集群中的 Kubernetes API server。
-Every namespace has a default service account, however, the `default` service account may not have the permission to create or delete pods within the Kubernetes cluster.
-Users may need to update the permission of `default` service account or specify another service account that has the right role bound.
+每个命名空间有默认的服务账户,但是`默认`服务账户可能没有权限在 Kubernetes 集群中创建或删除 pod。
+用户可能需要更新`默认`服务账户的权限或指定另一个绑定了正确角色的服务账户。
{% highlight bash %}
$ kubectl create clusterrolebinding flink-role-binding-default --clusterrole=edit --serviceaccount=default:default
{% endhighlight %}
-If you do not want to use `default` service account, use the following command to create a new `flink` service account and set the role binding.
-Then use the config option `-Dkubernetes.jobmanager.service-account=flink` to make the JobManager pod using the `flink` service account to create and delete TaskManager pods.
+如果你不想使用`默认`服务账户,使用以下命令创建一个新的 `flink` 服务账户并设置角色绑定。
+然后使用配置项 `-Dkubernetes.jobmanager.service-account=flink` 来使 JobManager pod 使用 `flink` 服务账户去创建和删除 TaskManager pod。
{% highlight bash %}
$ kubectl create serviceaccount flink
$ kubectl create clusterrolebinding flink-role-binding-flink --clusterrole=edit --serviceaccount=default:flink
{% endhighlight %}
-Please reference the official Kubernetes documentation on [RBAC Authorization](https://kubernetes.io/docs/reference/access-authn-authz/rbac/) for more information.
+有关更多信息,请参考 Kubernetes 官方文档 [RBAC 授权](https://kubernetes.io/docs/reference/access-authn-authz/rbac/)。
-## Background / Internals
+## 背景/内部构造
-This section briefly explains how Flink and Kubernetes interact.
+本节简要解释了 Flink 和 Kubernetes 如何交互。
-When creating a Flink Kubernetes session cluster, the Flink client will first connect to the Kubernetes ApiServer to submit the cluster description, including ConfigMap spec, Job Manager Service spec, Job Manager Deployment spec and Owner Reference.
-Kubernetes will then create the Flink master deployment, during which time the Kubelet will pull the image, prepare and mount the volume, and then execute the start command.
-After the master pod has launched, the Dispatcher and KubernetesResourceManager are available and the cluster is ready to accept one or more jobs.
+创建 Flink Kubernetes session 集群时,Flink 客户端首先将连接到 Kubernetes ApiServer 提交集群描述信息,包括 ConfigMap 描述信息、Job Manager Service 描述信息、Job Manager Deployment 描述信息和 Owner Reference。
+Kubernetes 将创建 JobManager 的 deployment,在此期间 Kubelet 将拉取镜像,准备并挂载卷,然后执行 start 命令。
+JobManager pod 启动后,Dispatcher 和 KubernetesResourceManager 服务会相继启动,然后集群准备完成,并等待提交作业。
-When users submit jobs through the Flink client, the job graph will be generated by the client and uploaded along with users jars to the Dispatcher.
-A JobMaster for that Job will be then be spawned.
+当用户通过 Flink 客户端提交作业时,将通过客户端生成 jobGraph 并将其与用户 jar 一起上传到 Dispatcher。
+然后 Dispatcher 会为每个 job 启动一个单独的 JobMaster。
-The JobMaster requests resources, known as slots, from the KubernetesResourceManager.
-If no slots are available, the resource manager will bring up TaskManager pods and registering them with the cluster.
+JobManager 向 KubernetesResourceManager 请求被称为 slots 的资源。
+如果没有可用的 slots,KubernetesResourceManager 将拉起 TaskManager pod 并且把它们注册到集群中。
{% top %}
diff --git a/docs/ops/deployment/yarn_setup.md b/docs/ops/deployment/yarn_setup.md
index 4dcd2903140798..27a29d63205dea 100644
--- a/docs/ops/deployment/yarn_setup.md
+++ b/docs/ops/deployment/yarn_setup.md
@@ -190,7 +190,7 @@ Action "run" compiles and runs a program.
method or "getPlan()" method. Only needed
if the JAR file does not specify the class
in its manifest.
- -m,--jobmanager Address of the JobManager (master) to
+ -m,--jobmanager Address of the JobManager to
which to connect. Use this flag to connect
to a different JobManager than the one
specified in the configuration.
@@ -222,7 +222,6 @@ You can check the number of TaskManagers in the JobManager web interface. The ad
If the TaskManagers do not show up after a minute, you should investigate the issue using the log files.
-
## Run a single Flink job on YARN
The documentation above describes how to start a Flink cluster within a Hadoop YARN environment. It is also possible to launch Flink within YARN only for executing a single job.
@@ -251,6 +250,48 @@ The user-jars position in the class path can be controlled by setting the parame
- `FIRST`: Adds the jar to the beginning of the system class path.
- `LAST`: Adds the jar to the end of the system class path.
+## Run an application in Application Mode
+
+To launch an application in [Application Mode]({% link ops/deployment/index.md %}#deployment-modes), you can type:
+
+{% highlight bash %}
+./bin/flink run-application -t yarn-application ./examples/batch/WordCount.jar
+{% endhighlight %}
+
+
+ Attention: Apart from the `-t`, all other configuration parameters, such as the path
+ to the savepoint to be used to bootstrap the application's state, the application parallelism or the
+ required job manager/task manager memory sizes, can be specified by their configuration option,
+ prefixed by `-D`.
+
+
+As an example, the command to specify the memory sizes of the JM and the TM, looks like:
+
+{% highlight bash %}
+./bin/flink run-application -t yarn-application \
+-Djobmanager.memory.process.size=2048m \
+-Dtaskmanager.memory.process.size=4096m \
+./examples/batch/WordCount.jar
+
+{% endhighlight %}
+
+For a look at the available configuration options, you can have a look [here]({% link ops/config.md %}). To unlock
+the full potential of the application mode, consider using it with the `yarn.provided.lib.dirs` configuration option
+and pre-upload your application jar to a location accessible by all nodes in your cluster. In this case, the
+command could look like:
+
+{% highlight bash %}
+./bin/flink run-application -t yarn-application \
+-Dyarn.provided.lib.dirs="hdfs://myhdfs/my-remote-flink-dist-dir" \
+hdfs://myhdfs/jars/my-application.jar
+{% endhighlight %}
+
+The above will allow the job submission to be extra lightweight as the needed Flink jars and the application jar
+are going to be picked up by the specified remote locations rather than be shipped to the cluster by the
+client.
+
+Stopping, cancelling or querying the status of a running application can be done in any of the existing ways.
+
## Recovery behavior of Flink on YARN
Flink's YARN client has the following configuration parameters to control how to behave in case of container failures. These parameters can be set either from the `conf/flink-conf.yaml` or when starting the YARN session, using `-D` parameters.
diff --git a/docs/ops/deployment/yarn_setup.zh.md b/docs/ops/deployment/yarn_setup.zh.md
index 7ff496b3fb705e..6e2a35d299789c 100644
--- a/docs/ops/deployment/yarn_setup.zh.md
+++ b/docs/ops/deployment/yarn_setup.zh.md
@@ -190,7 +190,7 @@ Action "run" compiles and runs a program.
method or "getPlan()" method. Only needed
if the JAR file does not specify the class
in its manifest.
- -m,--jobmanager Address of the JobManager (master) to
+ -m,--jobmanager Address of the JobManager to
which to connect. Use this flag to connect
to a different JobManager than the one
specified in the configuration.
@@ -222,7 +222,6 @@ You can check the number of TaskManagers in the JobManager web interface. The ad
If the TaskManagers do not show up after a minute, you should investigate the issue using the log files.
-
## Run a single Flink job on YARN
The documentation above describes how to start a Flink cluster within a Hadoop YARN environment. It is also possible to launch Flink within YARN only for executing a single job.
@@ -251,6 +250,44 @@ The user-jars position in the class path can be controlled by setting the parame
- `FIRST`: Adds the jar to the beginning of the system class path.
- `LAST`: Adds the jar to the end of the system class path.
+## Run an application in Application Mode
+
+To launch an application in [Application Mode]({% link ops/deployment/index.zh.md %}#deployment-modes), you can type:
+
+{% highlight bash %}
+./bin/flink run-application -t yarn-application ./examples/batch/WordCount.jar
+{% endhighlight %}
+
+
+ Attention: Apart from the `-t`, all other configuration parameters, such as the path
+ to the savepoint to be used to bootstrap the application's state, the application parallelism or the
+ required job manager/task manager memory sizes, can be specified by their configuration option,
+ prefixed by `-D`.
+
+
+As an example, the command to specify the memory sizes of the JM and the TM, looks like:
+
+{% highlight bash %}
+./bin/flink run-application -t yarn-application -Djobmanager.memory.process.size=2048m -Dtaskmanager.memory.process.size=4096m ./examples/batch/WordCount.jar
+{% endhighlight %}
+
+For a look at the available configuration options, you can have a look [here]({% link ops/config.zh.md %}). To unlock
+the full potential of the application mode, consider using it with the `yarn.provided.lib.dirs` configuration option
+and pre-upload your application jar to a location accessible by all nodes in your cluster. In this case, the
+command could look like:
+
+{% highlight bash %}
+./bin/flink run-application -t yarn-application \
+-Dyarn.provided.lib.dirs="hdfs://myhdfs/my-remote-flink-dist-dir" \
+hdfs://myhdfs/jars/my-application.jar
+{% endhighlight %}
+
+The above will allow the job submission to be extra lightweight as the needed Flink jars and the application jar
+are going to be picked up by the specified remote locations rather than be shipped to the cluster by the
+client.
+
+Stopping, cancelling or querying the status of a running application can be done in any of the existing ways.
+
## Recovery behavior of Flink on YARN
Flink's YARN client has the following configuration parameters to control how to behave in case of container failures. These parameters can be set either from the `conf/flink-conf.yaml` or when starting the YARN session, using `-D` parameters.
@@ -331,4 +368,4 @@ The *JobManager* and AM are running in the same container. Once they successfull
After that, the AM starts allocating the containers for Flink's TaskManagers, which will download the jar file and the modified configuration from the HDFS. Once these steps are completed, Flink is set up and ready to accept Jobs.
-{% top %}
\ No newline at end of file
+{% top %}
diff --git a/docs/ops/external_resources.md b/docs/ops/external_resources.md
new file mode 100644
index 00000000000000..c4fab3623e39a8
--- /dev/null
+++ b/docs/ops/external_resources.md
@@ -0,0 +1,360 @@
+---
+title: "External Resource Framework"
+nav-parent_id: ops
+nav-pos: 10
+nav-title: External Resources
+---
+
+
+In addition to CPU and memory, many workloads also need some other resources, e.g. GPUs for deep learning. To support external
+resources, Flink provides an external resource framework. The framework supports requesting various types of resources from the
+underlying resource management systems (e.g., Kubernetes), and supplies information needed for using these resources to the operators.
+Different resource types can be supported. You can either leverage built-in plugins provided by Flink (currently only for GPU support),
+or implement your own plugins for custom resource types.
+
+* This will be replaced by the TOC
+{:toc}
+
+# What the external resource framework does
+
+In general, the external resource framework does two things:
+
+ - Set the corresponding fields of the resource requests (for requesting resources from the underlying system) with respect to your configuration.
+
+ - Provide operators with the *information* needed for using the resources.
+
+When deployed on resource management systems (Kubernetes / Yarn), the external resource framework will ensure that the allocated pod/container
+will contain the desired external resources. Currently, many resource management systems support external resources. For example,
+Kubernetes supports GPU, FPGA, etc. through its [Device Plugin](https://kubernetes.io/docs/concepts/extend-kubernetes/compute-storage-net/device-plugins/)
+mechanism since v1.10, and Yarn supports GPU and FPGA resources since 2.10 and 3.1. External resources are not supported by Flink’s Mesos
+integration at the moment. In Standalone mode, the user has to ensure that the external resources are available.
+
+The external resource framework will provide the corresponding *information* to operators. The external resource information,
+which contains the basic properties needed for using the resources, is generated by the configured external resource *drivers*.
+
+# Enable the external resource framework for your workload
+
+To enable an external resource with the external resource framework, you need to:
+
+ - Prepare the external resource *plugin*.
+
+ - Set configurations for the external resource.
+
+ - Get the external resource *information* from `RuntimeContext` and use it in your operators.
+
+## Prepare plugins
+
+You need to prepare the external resource plugin and put it into the `plugins/` folder of your Flink distribution, see
+[Flink Plugins]({% link ops/plugins.md %}). Apache Flink provides a first-party [plugin for GPU resources](#plugin-for-gpu-resources). You can also
+[implement a plugin for your custom resource type](#implement-a-plugin-for-your-custom-resource-type).
+
+## Configurations
+
+First, you need to add resource names for all the external resource types to the **external resource list (with the configuration key ‘external-resources’)**
+with delimiter ";", e.g. "external-resources: gpu;fpga" for two external resources "gpu" and "fpga". Only the **\**
+defined here will go into effect in the external resource framework.
+
+For each external resource, you could configure the below options. The **\** in all the below configuration options
+corresponds to the name listed in the **external resource list**:
+
+ - **Amount** (`external..amount`): This is the quantity of the external resource that should be requested from the external system.
+
+ - **Config key in Yarn** (`external-resource..yarn.config-key`): *optional*. If configured, the external
+ resource framework will add this key to the resource profile of container requests for Yarn. The value will be set to the
+ value of `external-resource..amount`.
+
+ - **Config key in Kubernetes** (`external-resource..kubernetes.config-key`): *optional*. If configured,
+ external resource framework will add `resources.limits.` and `resources.requests.` to the main
+ container spec of TaskExecutor and set the value to the value of `external-resource..amount`.
+
+ - **Driver Factory** (`external-resource..driver-factory.class`): *optional*. Defines the factory class
+ name for the external resource identified by **\**. If configured, the factory will be used to instantiate
+ *drivers* in the external resource framework.
+
+ - **Driver Parameters** (`external-resource..param.`): *optional*. The naming pattern of custom
+ config options for the external resource specified by **\**. Only the configurations that follow this pattern
+ will be passed into the *driver* factory of that external resource.
+
+An example configuration that specifies two external resources:
+
+{% highlight bash %}
+external-resources: gpu;fpga # Define two external resources, "gpu" and "fpga".
+
+external-resource.gpu.driver-factory.class: org.apache.flink.externalresource.gpu.GPUDriverFactory # Define the driver factory class of gpu resource.
+external-resource.gpu.amount: 2 # Define the amount of gpu resource per TaskManager.
+external-resource.gpu.param.discovery-script.args: --enable-coordination # Define the custom param discovery-script.args which will be passed into the gpu driver.
+
+external-resource.fpga.driver-factory.class: org.apache.flink.externalresource.fpga.FPGADriverFactory # Define the driver factory class of fpga resource.
+external-resource.fpga.amount: 1 # Define the amount of fpga resource per TaskManager.
+external-resource.fpga.yarn.config-key: yarn.io/fpga # Define the corresponding config key of fpga in Yarn.
+{% endhighlight %}
+
+## Use the resources
+
+To use the resources, operators need to get the `ExternalResourceInfo` set from the `RuntimeContext`. `ExternalResourceInfo`
+wraps the information needed for using the resource, which can be retrieved with `getProperty`. What properties are available
+and how to access the resource with the properties depends on the specific plugin.
+
+Operators can get the `ExternalResourceInfo` set of a specific external resource from `RuntimeContext` or `FunctionContext` by
+`getExternalResourceInfos(String resourceName)`. The `resourceName` here should have the same value as the name configured in the
+external resource list. It can be used as follows:
+
+
+
+{% highlight java %}
+public class ExternalResourceMapFunction extends RichMapFunction {
+ private static final String RESOURCE_NAME = "foo";
+
+ @Override
+ public String map(String value) {
+ Set externalResourceInfos = getRuntimeContext().getExternalResourceInfos(RESOURCE_NAME);
+ List addresses = new ArrayList<>();
+ externalResourceInfos.iterator().forEachRemaining(externalResourceInfo ->
+ addresses.add(externalResourceInfo.getProperty("address").get()));
+ // map function with addresses.
+ // ...
+ }
+}
+{% endhighlight %}
+
+
+
+{% highlight scala %}
+class ExternalResourceMapFunction extends RichMapFunction[(String, String)] {
+ var RESOURCE_NAME = "foo"
+
+ override def map(value: String): String = {
+ val externalResourceInfos = getRuntimeContext().getExternalResourceInfos(RESOURCE_NAME)
+ val addresses = new util.ArrayList[String]
+ externalResourceInfos.asScala.foreach(
+ externalResourceInfo => addresses.add(externalResourceInfo.getProperty("address").get()))
+
+ // map function with addresses.
+ // ...
+ }
+}
+{% endhighlight %}
+
+
+
+Each `ExternalResourceInfo` contains one or more properties with keys representing the different dimensions of the resource.
+You could get all valid keys by `ExternalResourceInfo#getKeys`.
+
+
+ Note: Currently, the information returned by `RuntimeContext#getExternalResourceInfos` is available to all the operators.
+
+
+# Implement a plugin for your custom resource type
+
+To implement a plugin for your custom resource type, you need to:
+
+ - Add your own external resource driver by implementing the `org.apache.flink.api.common.externalresource.ExternalResourceDriver` interface.
+
+ - Add a driver factory, which instantiates the *driver*, by implementing the `org.apache.flink.api.common.externalresource.ExternalResourceDriverFactory`.
+
+ - Add a service entry. Create a file `META-INF/services/org.apache.flink.api.common.externalresource.ExternalResourceDriverFactory`
+ which contains the class name of your *driver* factory class (see the [Java Service Loader](https://docs.oracle.com/javase/8/docs/api/java/util/ServiceLoader.html) docs for more details).
+
+For example, to implement a plugin for external resource named "FPGA", you need to implement `FPGADriver` and `FPGADriverFactory` first:
+
+
+
+{% highlight java %}
+public class FPGADriver implements ExternalResourceDriver {
+ @Override
+ public Set retrieveResourceInfo(long amount) {
+ // return the information set of "FPGA"
+ }
+}
+
+public class FPGADriverFactory implements ExternalResourceDriverFactory {
+ @Override
+ public ExternalResourceDriver createExternalResourceDriver(Configuration config) {
+ return new FPGADriver();
+ }
+}
+
+// Also implement FPGAInfo which contains basic properties of "FPGA" resource.
+public class FPGAInfo implements ExternalResourceInfo {
+ @Override
+ public Optional getProperty(String key) {
+ // return the property with the given key.
+ }
+
+ @Override
+ public Collection getKeys() {
+ // return all property keys.
+ }
+}
+{% endhighlight %}
+
+
+
+{% highlight scala %}
+class FPGADriver extends ExternalResourceDriver {
+ override def retrieveResourceInfo(amount: Long): Set[FPGAInfo] = {
+ // return the information set of "FPGA"
+ }
+}
+
+class FPGADriverFactory extends ExternalResourceDriverFactory {
+ override def createExternalResourceDriver(config: Configuration): ExternalResourceDriver = {
+ new FPGADriver()
+ }
+}
+
+// Also implement FPGAInfo which contains basic properties of "FPGA" resource.
+class FPGAInfo extends ExternalResourceInfo {
+ override def getProperty(key: String): Option[String] = {
+ // return the property with the given key.
+ }
+
+ override def getKeys(): util.Collection[String] = {
+ // return all property keys.
+ }
+}
+{% endhighlight %}
+
+
+
+Create a file with name `org.apache.flink.api.common.externalresource.ExternalResourceDriverFactory` in `META-INF/services/`
+and write the factory class name (e.g. `your.domain.FPGADriverFactory`) to it.
+
+Then, create a jar which includes `FPGADriver`, `FPGADriverFactory`, `META-INF/services/` and all the external dependencies.
+Make a directory in `plugins/` of your Flink distribution with an arbitrary name, e.g. "fpga", and put the jar into this directory.
+See [Flink Plugin]({% link ops/plugins.md %}) for more details.
+
+
+ Note: External resources are shared by all operators running on the same machine. The community might add external resource isolation in a future release.
+
+
+# Existing supported external resource plugins
+
+Currently, Flink supports GPUs as external resources.
+
+## Plugin for GPU resources
+
+We provide a first-party plugin for GPU resources. The plugin leverages a discovery script to discover indexes of GPU devices, which can
+be accessed from the resource *information* via the property "index". We provide a default discovery script that can be used to discover
+NVIDIA GPUs. You can also provide your custom script.
+
+We provide [an example](https://github.com/apache/flink/blob/{{ site.github_branch }}/flink-examples/flink-examples-streaming/src/main/java/org/apache/flink/streaming/examples/gpu/MatrixVectorMul.java)
+which shows how to use the GPUs to do matrix-vector multiplication in Flink.
+
+
+ Note: Currently, for all the operators, RuntimeContext#getExternalResourceInfos returns the same set of resource information. That means, the same set of GPU devices are always accessible to all the operators running in the same TaskManager. There is no operator level isolation at the moment.
+
+
+### Pre-requisites
+
+To make GPU resources accessible, certain prerequisites are needed depending on your environment:
+
+ - For standalone mode, administrators should ensure the NVIDIA driver is installed and GPU resources are accessible on all the nodes in the cluster.
+
+ - For Yarn deployment, administrators should configure the Yarn cluster to enable [GPU scheduling](https://hadoop.apache.org/docs/current/hadoop-yarn/hadoop-yarn-site/UsingGpus.html).
+ Notice the required Hadoop version is 2.10+ or 3.1+.
+
+ - For Kubernetes deployment, administrators should make sure the NVIDIA GPU [device plugin](https://kubernetes.io/docs/concepts/extend-kubernetes/compute-storage-net/device-plugins/)
+ is installed. Notice the required version is 1.10+. At the moment, Kubernetes only supports NVIDIA GPU and AMD GPU. Flink only provides discovery script for NVIDIA GPUs,
+ but you can provide a custom discovery script for AMD GPUs yourself, see [Discovery script](#discovery-script).
+
+### Enable GPU resources for your workload
+
+As mentioned in [Enable external resources for your workload](#enable-the-external-resource-framework-for-your-workload),
+you also need to do two things to enable GPU resources:
+
+ - Configure the GPU resource.
+
+ - Get the *information* of GPU resources, which contains the GPU index as property with key "index", in operators.
+
+### Configurations
+
+For the GPU plugin, you need to specify the common external resource configurations:
+
+ - `external-resources`: You need to append your resource name (e.g. gpu) for GPU resources to it.
+
+ - `external-resource..amount`: The amount of GPU devices per TaskManager.
+
+ - `external-resource..yarn.config-key`: For Yarn, the config key of GPU is `yarn.io/gpu`. Notice that
+ Yarn only supports NVIDIA GPU at the moment.
+
+ - `external-resource..kubernetes.config-key`: For Kubernetes, the config key of GPU is `.com/gpu`.
+ Currently, "nvidia" and "amd" are the two supported vendors. Notice that if you use AMD GPUs, you need to provide a discovery
+ script yourself, see [Discovery script](#discovery-script).
+
+ - external-resource..driver-factory.class: Should be set to org.apache.flink.externalresource.gpu.GPUDriverFactory.
+
+In addition, there are some specific configurations for the GPU plugin:
+
+ - `external-resource..param.discovery-script.path`: The path of the [discovery script](#discovery-script). It
+ can either be an absolute path, or a relative path to `FLINK_HOME` when defined or current directory otherwise. If not
+ explicitly configured, the default script will be used.
+
+ - `external-resource..param.discovery-script.args`: The arguments passed to the discovery script. For the default
+ discovery script, see [Default Script](#default-script) for the available parameters.
+
+An example configuration for GPU resource:
+
+{% highlight bash %}
+external-resources: gpu
+external-resource.gpu.driver-factory.class: org.apache.flink.externalresource.gpu.GPUDriverFactory # Define the driver factory class of gpu resource.
+external-resource.gpu.amount: 2 # Define the amount of gpu resource per TaskManager.
+external-resource.gpu.param.discovery-script.path: plugins/external-resource-gpu/nvidia-gpu-discovery.sh
+external-resource.gpu.param.discovery-script.args: --enable-coordination # Define the custom param "discovery-script.args" which will be passed into the gpu driver.
+
+external-resource.gpu.yarn.config-key: yarn.io/gpu # for Yarn
+
+external-resource.gpu.kubernetes.config-key: nvidia.com/gpu # for Kubernetes
+{% endhighlight %}
+
+### Discovery script
+
+The `GPUDriver` leverages a discovery script to discover GPU resources and generate the GPU resource information.
+
+#### Default Script
+
+We provide a default discovery script for NVIDIA GPU, located at `plugins/external-resource-gpu/nvidia-gpu-discovery.sh` of your
+Flink distribution. The script gets the indexes of visible GPU resources through the `nvidia-smi` command. It tries to return
+the required amount (specified by `external-resource..amount`) of GPU indexes in a list, and exit with non-zero if the amount cannot be satisfied.
+
+For standalone mode, multiple TaskManagers might be co-located on the same machine, and each GPU device is visible to all
+the TaskManagers. The default discovery script supports a coordination mode, in which it leverages a coordination file to
+synchronize the allocation state of GPU devices and ensure each GPU device can only be used by one TaskManager process. The relevant arguments are:
+
+ - `--enable-coordination-mode`: Enable the coordination mode.
+
+ - `--coordination-file filePath`: The path of the coordination file used to synchronize the allocation state of GPU resources. The default path is `/var/tmp/flink-gpu-coordination`.
+
+
+ Note: The coordination mode only ensures that a GPU device is not shared by multiple TaskManagers of the same Flink cluster. Please be aware that another Flink cluster (with a different coordination file) or a non-Flink application can still use the same GPU devices.
+
+
+#### Custom Script
+
+You can also provide a discovery script to address your custom requirements, e.g. discovering AMD GPU. Please make sure
+the path of your custom script is accessible to Flink and configured (`external-resource..param.discovery-script.path`) correctly.
+The contract of the discovery script:
+
+ - `GPUDriver` passes the amount (specified by `external-resource..amount`) as the first argument into the script.
+ The user-defined arguments in `external-resource..param.discovery-script.args` would be appended after it.
+
+ - The script should return a list of the available GPU indexes, split by a comma. Whitespace only indexes will be ignored.
+
+ - The script can also suggest that the discovery is not properly performed, by exiting with non-zero. In that case, no gpu information will be provided to operators.
diff --git a/docs/ops/external_resources.zh.md b/docs/ops/external_resources.zh.md
new file mode 100644
index 00000000000000..bb9ebce4d184d5
--- /dev/null
+++ b/docs/ops/external_resources.zh.md
@@ -0,0 +1,360 @@
+---
+title: "外部资源调度框架"
+nav-parent_id: ops
+nav-pos: 10
+nav-title: External Resources
+---
+
+
+In addition to CPU and memory, many workloads also need some other resources, e.g. GPUs for deep learning. To support external
+resources, Flink provides an external resource framework. The framework supports requesting various types of resources from the
+underlying resource management systems (e.g., Kubernetes), and supplies information needed for using these resources to the operators.
+Different resource types can be supported. You can either leverage built-in plugins provided by Flink (currently only for GPU support),
+or implement your own plugins for custom resource types.
+
+* This will be replaced by the TOC
+{:toc}
+
+# What the external resource framework does
+
+In general, the external resource framework does two things:
+
+ - Set the corresponding fields of the resource requests (for requesting resources from the underlying system) with respect to your configuration.
+
+ - Provide operators with the *information* needed for using the resources.
+
+When deployed on resource management systems (Kubernetes / Yarn), the external resource framework will ensure that the allocated pod/container
+will contain the desired external resources. Currently, many resource management systems support external resources. For example,
+Kubernetes supports GPU, FPGA, etc. through its [Device Plugin](https://kubernetes.io/docs/concepts/extend-kubernetes/compute-storage-net/device-plugins/)
+mechanism since v1.10, and Yarn supports GPU and FPGA resources since 2.10 and 3.1. External resources are not supported by Flink’s Mesos
+integration at the moment. In Standalone mode, the user has to ensure that the external resources are available.
+
+The external resource framework will provide the corresponding *information* to operators. The external resource information,
+which contains the basic properties needed for using the resources, is generated by the configured external resource *drivers*.
+
+# Enable the external resource framework for your workload
+
+To enable an external resource with the external resource framework, you need to:
+
+ - Prepare the external resource *plugin*.
+
+ - Set configurations for the external resource.
+
+ - Get the external resource *information* from `RuntimeContext` and use it in your operators.
+
+## Prepare plugins
+
+You need to prepare the external resource plugin and put it into the `plugins/` folder of your Flink distribution, see
+[Flink Plugins]({% link ops/plugins.zh.md %}). Apache Flink provides a first-party [plugin for GPU resources](#plugin-for-gpu-resources). You can also
+[implement a plugin for your custom resource type](#implement-a-plugin-for-your-custom-resource-type).
+
+## Configurations
+
+First, you need to add resource names for all the external resource types to the **external resource list (with the configuration key ‘external-resources’)**
+with delimiter ";", e.g. "external-resources: gpu;fpga" for two external resources "gpu" and "fpga". Only the **\**
+defined here will go into effect in the external resource framework.
+
+For each external resource, you could configure the below options. The **\** in all the below configuration options
+corresponds to the name listed in the **external resource list**:
+
+ - **Amount** (`external..amount`): This is the quantity of the external resource that should be requested from the external system.
+
+ - **Config key in Yarn** (`external-resource..yarn.config-key`): *optional*. If configured, the external
+ resource framework will add this key to the resource profile of container requests for Yarn. The value will be set to the
+ value of `external-resource..amount`.
+
+ - **Config key in Kubernetes** (`external-resource..kubernetes.config-key`): *optional*. If configured,
+ external resource framework will add `resources.limits.` and `resources.requests.` to the main
+ container spec of TaskExecutor and set the value to the value of `external-resource..amount`.
+
+ - **Driver Factory** (`external-resource..driver-factory.class`): *optional*. Defines the factory class
+ name for the external resource identified by **\**. If configured, the factory will be used to instantiate
+ *drivers* in the external resource framework.
+
+ - **Driver Parameters** (`external-resource..param.`): *optional*. The naming pattern of custom
+ config options for the external resource specified by **\**. Only the configurations that follow this pattern
+ will be passed into the *driver* factory of that external resource.
+
+An example configuration that specifies two external resources:
+
+{% highlight bash %}
+external-resources: gpu;fpga # Define two external resources, "gpu" and "fpga".
+
+external-resource.gpu.driver-factory.class: org.apache.flink.externalresource.gpu.GPUDriverFactory # Define the driver factory class of gpu resource.
+external-resource.gpu.amount: 2 # Define the amount of gpu resource per TaskManager.
+external-resource.gpu.param.discovery-script.args: --enable-coordination # Define the custom param discovery-script.args which will be passed into the gpu driver.
+
+external-resource.fpga.driver-factory.class: org.apache.flink.externalresource.fpga.FPGADriverFactory # Define the driver factory class of fpga resource.
+external-resource.fpga.amount: 1 # Define the amount of fpga resource per TaskManager.
+external-resource.fpga.yarn.config-key: yarn.io/fpga # Define the corresponding config key of fpga in Yarn.
+{% endhighlight %}
+
+## Use the resources
+
+To use the resources, operators need to get the `ExternalResourceInfo` set from the `RuntimeContext`. `ExternalResourceInfo`
+wraps the information needed for using the resource, which can be retrieved with `getProperty`. What properties are available
+and how to access the resource with the properties depends on the specific plugin.
+
+Operators can get the `ExternalResourceInfo` set of a specific external resource from `RuntimeContext` or `FunctionContext` by
+`getExternalResourceInfos(String resourceName)`. The `resourceName` here should have the same value as the name configured in the
+external resource list. It can be used as follows:
+
+
+
+{% highlight java %}
+public class ExternalResourceMapFunction extends RichMapFunction {
+ private static final String RESOURCE_NAME = "foo";
+
+ @Override
+ public String map(String value) {
+ Set externalResourceInfos = getRuntimeContext().getExternalResourceInfos(RESOURCE_NAME);
+ List addresses = new ArrayList<>();
+ externalResourceInfos.iterator().forEachRemaining(externalResourceInfo ->
+ addresses.add(externalResourceInfo.getProperty("address").get()));
+ // map function with addresses.
+ // ...
+ }
+}
+{% endhighlight %}
+
+
+
+{% highlight scala %}
+class ExternalResourceMapFunction extends RichMapFunction[(String, String)] {
+ var RESOURCE_NAME = "foo"
+
+ override def map(value: String): String = {
+ val externalResourceInfos = getRuntimeContext().getExternalResourceInfos(RESOURCE_NAME)
+ val addresses = new util.ArrayList[String]
+ externalResourceInfos.asScala.foreach(
+ externalResourceInfo => addresses.add(externalResourceInfo.getProperty("address").get()))
+
+ // map function with addresses.
+ // ...
+ }
+}
+{% endhighlight %}
+
+
+
+Each `ExternalResourceInfo` contains one or more properties with keys representing the different dimensions of the resource.
+You could get all valid keys by `ExternalResourceInfo#getKeys`.
+
+
+ Note: Currently, the information returned by `RuntimeContext#getExternalResourceInfos` is available to all the operators.
+
+
+# Implement a plugin for your custom resource type
+
+To implement a plugin for your custom resource type, you need to:
+
+ - Add your own external resource driver by implementing the `org.apache.flink.api.common.externalresource.ExternalResourceDriver` interface.
+
+ - Add a driver factory, which instantiates the *driver*, by implementing the `org.apache.flink.api.common.externalresource.ExternalResourceDriverFactory`.
+
+ - Add a service entry. Create a file `META-INF/services/org.apache.flink.api.common.externalresource.ExternalResourceDriverFactory`
+ which contains the class name of your *driver* factory class (see the [Java Service Loader](https://docs.oracle.com/javase/8/docs/api/java/util/ServiceLoader.html) docs for more details).
+
+For example, to implement a plugin for external resource named "FPGA", you need to implement `FPGADriver` and `FPGADriverFactory` first:
+
+
+
+{% highlight java %}
+public class FPGADriver implements ExternalResourceDriver {
+ @Override
+ public Set retrieveResourceInfo(long amount) {
+ // return the information set of "FPGA"
+ }
+}
+
+public class FPGADriverFactory implements ExternalResourceDriverFactory {
+ @Override
+ public ExternalResourceDriver createExternalResourceDriver(Configuration config) {
+ return new FPGADriver();
+ }
+}
+
+// Also implement FPGAInfo which contains basic properties of "FPGA" resource.
+public class FPGAInfo implements ExternalResourceInfo {
+ @Override
+ public Optional getProperty(String key) {
+ // return the property with the given key.
+ }
+
+ @Override
+ public Collection getKeys() {
+ // return all property keys.
+ }
+}
+{% endhighlight %}
+
+
+
+{% highlight scala %}
+class FPGADriver extends ExternalResourceDriver {
+ override def retrieveResourceInfo(amount: Long): Set[FPGAInfo] = {
+ // return the information set of "FPGA"
+ }
+}
+
+class FPGADriverFactory extends ExternalResourceDriverFactory {
+ override def createExternalResourceDriver(config: Configuration): ExternalResourceDriver = {
+ new FPGADriver()
+ }
+}
+
+// Also implement FPGAInfo which contains basic properties of "FPGA" resource.
+class FPGAInfo extends ExternalResourceInfo {
+ override def getProperty(key: String): Option[String] = {
+ // return the property with the given key.
+ }
+
+ override def getKeys(): util.Collection[String] = {
+ // return all property keys.
+ }
+}
+{% endhighlight %}
+
+
+
+Create a file with name `org.apache.flink.api.common.externalresource.ExternalResourceDriverFactory` in `META-INF/services/`
+and write the factory class name (e.g. `your.domain.FPGADriverFactory`) to it.
+
+Then, create a jar which includes `FPGADriver`, `FPGADriverFactory`, `META-INF/services/` and all the external dependencies.
+Make a directory in `plugins/` of your Flink distribution with an arbitrary name, e.g. "fpga", and put the jar into this directory.
+See [Flink Plugin]({% link ops/plugins.zh.md %}) for more details.
+
+
+ Note: External resources are shared by all operators running on the same machine. The community might add external resource isolation in a future release.
+
+
+# Existing supported external resource plugins
+
+Currently, Flink supports GPUs as external resources.
+
+## Plugin for GPU resources
+
+We provide a first-party plugin for GPU resources. The plugin leverages a discovery script to discover indexes of GPU devices, which can
+be accessed from the resource *information* via the property "index". We provide a default discovery script that can be used to discover
+NVIDIA GPUs. You can also provide your custom script.
+
+We provide [an example](https://github.com/apache/flink/blob/master/flink-examples/flink-examples-streaming/src/main/java/org/apache/flink/streaming/examples/gpu/MatrixVectorMul.java)
+which shows how to use the GPUs to do matrix-vector multiplication in Flink.
+
+
+ Note: Currently, for all the operators, RuntimeContext#getExternalResourceInfos returns the same set of resource information. That means, the same set of GPU devices are always accessible to all the operators running in the same TaskManager. There is no operator level isolation at the moment.
+
+
+### Pre-requisites
+
+To make GPU resources accessible, certain prerequisites are needed depending on your environment:
+
+ - For standalone mode, administrators should ensure the NVIDIA driver is installed and GPU resources are accessible on all the nodes in the cluster.
+
+ - For Yarn deployment, administrators should configure the Yarn cluster to enable [GPU scheduling](https://hadoop.apache.org/docs/current/hadoop-yarn/hadoop-yarn-site/UsingGpus.html).
+ Notice the required Hadoop version is 2.10+ or 3.1+.
+
+ - For Kubernetes deployment, administrators should make sure the NVIDIA GPU [device plugin](https://kubernetes.io/docs/concepts/extend-kubernetes/compute-storage-net/device-plugins/)
+ is installed. Notice the required version is 1.10+. At the moment, Kubernetes only supports NVIDIA GPU and AMD GPU. Flink only provides discovery script for NVIDIA GPUs,
+ but you can provide a custom discovery script for AMD GPUs yourself, see [Discovery script](#discovery-script).
+
+### Enable GPU resources for your workload
+
+As mentioned in [Enable external resources for your workload](#enable-the-external-resource-framework-for-your-workload),
+you also need to do two things to enable GPU resources:
+
+ - Configure the GPU resource.
+
+ - Get the *information* of GPU resources, which contains the GPU index as property with key "index", in operators.
+
+### Configurations
+
+For the GPU plugin, you need to specify the common external resource configurations:
+
+ - `external-resources`: You need to append your resource name (e.g. gpu) for GPU resources to it.
+
+ - `external-resource..amount`: The amount of GPU devices per TaskManager.
+
+ - `external-resource..yarn.config-key`: For Yarn, the config key of GPU is `yarn.io/gpu`. Notice that
+ Yarn only supports NVIDIA GPU at the moment.
+
+ - `external-resource..kubernetes.config-key`: For Kubernetes, the config key of GPU is `.com/gpu`.
+ Currently, "nvidia" and "amd" are the two supported vendors. Notice that if you use AMD GPUs, you need to provide a discovery
+ script yourself, see [Discovery script](#discovery-script).
+
+ - external-resource..driver-factory.class: Should be set to org.apache.flink.externalresource.gpu.GPUDriverFactory.
+
+In addition, there are some specific configurations for the GPU plugin:
+
+ - `external-resource..param.discovery-script.path`: The path of the [discovery script](#discovery-script). It
+ can either be an absolute path, or a relative path to `FLINK_HOME` when defined or current directory otherwise. If not
+ explicitly configured, the default script will be used.
+
+ - `external-resource..param.discovery-script.args`: The arguments passed to the discovery script. For the default
+ discovery script, see [Default Script](#default-script) for the available parameters.
+
+An example configuration for GPU resource:
+
+{% highlight bash %}
+external-resources: gpu
+external-resource.gpu.driver-factory.class: org.apache.flink.externalresource.gpu.GPUDriverFactory # Define the driver factory class of gpu resource.
+external-resource.gpu.amount: 2 # Define the amount of gpu resource per TaskManager.
+external-resource.gpu.param.discovery-script.path: plugins/external-resource-gpu/nvidia-gpu-discovery.sh
+external-resource.gpu.param.discovery-script.args: --enable-coordination # Define the custom param "discovery-script.args" which will be passed into the gpu driver.
+
+external-resource.gpu.yarn.config-key: yarn.io/gpu # for Yarn
+
+external-resource.gpu.kubernetes.config-key: nvidia.com/gpu # for Kubernetes
+{% endhighlight %}
+
+### Discovery script
+
+The `GPUDriver` leverages a discovery script to discover GPU resources and generate the GPU resource information.
+
+#### Default Script
+
+We provide a default discovery script for NVIDIA GPU, located at `plugins/external-resource-gpu/nvidia-gpu-discovery.sh` of your
+Flink distribution. The script gets the indexes of visible GPU resources through the `nvidia-smi` command. It tries to return
+the required amount (specified by `external-resource..amount`) of GPU indexes in a list, and exit with non-zero if the amount cannot be satisfied.
+
+For standalone mode, multiple TaskManagers might be co-located on the same machine, and each GPU device is visible to all
+the TaskManagers. The default discovery script supports a coordination mode, in which it leverages a coordination file to
+synchronize the allocation state of GPU devices and ensure each GPU device can only be used by one TaskManager process. The relevant arguments are:
+
+ - `--enable-coordination-mode`: Enable the coordination mode.
+
+ - `--coordination-file filePath`: The path of the coordination file used to synchronize the allocation state of GPU resources. The default path is `/var/tmp/flink-gpu-coordination`.
+
+
+ Note: The coordination mode only ensures that a GPU device is not shared by multiple TaskManagers of the same Flink cluster. Please be aware that another Flink cluster (with a different coordination file) or a non-Flink application can still use the same GPU devices.
+
+
+#### Custom Script
+
+You can also provide a discovery script to address your custom requirements, e.g. discovering AMD GPU. Please make sure
+the path of your custom script is accessible to Flink and configured (`external-resource..param.discovery-script.path`) correctly.
+The contract of the discovery script:
+
+ - `GPUDriver` passes the amount (specified by `external-resource..amount`) as the first argument into the script.
+ The user-defined arguments in `external-resource..param.discovery-script.args` would be appended after it.
+
+ - The script should return a list of the available GPU indexes, split by a comma. Whitespace only indexes will be ignored.
+
+ - The script can also suggest that the discovery is not properly performed, by exiting with non-zero. In that case, no gpu information will be provided to operators.
diff --git a/docs/ops/memory/mem_migration.md b/docs/ops/memory/mem_migration.md
index 81229f408ce5cf..94c04fe2724019 100644
--- a/docs/ops/memory/mem_migration.md
+++ b/docs/ops/memory/mem_migration.md
@@ -23,10 +23,10 @@ under the License.
-->
The memory setup has changed a lot with the *1.10* release for [TaskManagers](mem_setup_tm.html) and with the *1.11*
-release for [Masters](mem_setup_master.html). Many configuration options were removed or their semantics changed.
+release for [JobManagers]({% link ops/memory/mem_setup_jobmanager.md %}). Many configuration options were removed or their semantics changed.
This guide will help you to migrate the TaskManager memory configuration from Flink
[<= *1.9*](https://ci.apache.org/projects/flink/flink-docs-release-1.9/ops/mem_setup.html) to >= *1.10* and
-the Master memory configuration from Flink <= *1.10* to >= *1.11*.
+the JobManager memory configuration from Flink <= *1.10* to >= *1.11*.
* toc
{:toc}
@@ -34,16 +34,16 @@ the Master memory configuration from Flink <= *1.10* to >= *1.11*.
Warning: It is important to review this guide because the legacy and new memory configuration can
result in different sizes of memory components. If you try to reuse your Flink configuration from older versions
- before 1.10 for TaskManagers or before 1.11 for Masters, it can result in changes to the behavior,
+ before 1.10 for TaskManagers or before 1.11 for JobManagers, it can result in changes to the behavior,
performance or even configuration failures of your application.
-Note Before version *1.10* for TaskManagers and before *1.11* for Masters,
+Note Before version *1.10* for TaskManagers and before *1.11* for JobManagers,
Flink did not require that memory related options are set at all as they all had default values.
The [new memory configuration](mem_setup.html#configure-total-memory) requires that at least one subset of
the following options is configured explicitly, otherwise the configuration will fail:
-| **for TaskManager:** | **for Master:** |
+| **for TaskManager:** | **for JobManager:** |
| :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :-------------------------------------------------------------------------------- |
| [`taskmanager.memory.flink.size`](../config.html#taskmanager-memory-flink-size) | [`jobmanager.memory.flink.size`](../config.html#jobmanager-memory-flink-size) |
| [`taskmanager.memory.process.size`](../config.html#taskmanager-memory-process-size) | [`jobmanager.memory.process.size`](../config.html#jobmanager-memory-process-size) |
@@ -59,7 +59,7 @@ to make the default memory configuration consistent.
This [spreadsheet](https://docs.google.com/spreadsheets/d/1mJaMkMPfDJJ-w6nMXALYmTc4XxiV30P5U7DzgwLkSoE) can also help
to evaluate and compare the results of the legacy and new memory computations.
-## Migrate Task Executor Memory Configuration
+## Migrate Task Manager Memory Configuration
### Changes in Configuration Options
@@ -79,7 +79,7 @@ The following options are completely removed. If they are still used, they will
@@ -139,7 +139,7 @@ It can change if other memory components have new sizes, e.g. the total memory w
See also [new detailed memory model](mem_setup_tm.html#detailed-memory-model).
The container cut-off configuration options, `containerized.heap-cutoff-ratio` and `containerized.heap-cutoff-min`,
-have no effect anymore. See also [how to migrate container cut-off](#container-cut-off-memory).
+have no effect anymore for TaskManagers. See also [how to migrate container cut-off](#container-cut-off-memory).
### Total Memory (Previously Heap Memory)
@@ -214,7 +214,7 @@ Additionally, the following changes have been made:
## Migrate Job Manager Memory Configuration
-Previously, there were options responsible for setting the *JVM Heap* size of the Flink Master:
+Previously, there were options responsible for setting the *JVM Heap* size of the JobManager:
* `jobmanager.heap.size`
* `jobmanager.heap.mb`
@@ -224,21 +224,35 @@ they also included other off-heap memory consumption. The size of *JVM Heap* was
cut-off which has been completely removed after *1.11*.
The [Mesos](../deployment/mesos.html) integration did not take into account the mentioned legacy memory options.
-The scripts provided in Flink to start the Mesos Master process did not set any memory JVM arguments. After the *1.11* release,
+The scripts provided in Flink to start the Mesos JobManager process did not set any memory JVM arguments. After the *1.11* release,
they are set the same way as it is done by the [standalone deployment](../deployment/cluster_setup.html) scripts.
The mentioned legacy options have been deprecated. If they are used without specifying the corresponding new options,
they will be directly translated into the following new options:
* JVM Heap ([`jobmanager.memory.heap.size`](../config.html#jobmanager-memory-heap-size)) for [standalone](../deployment/cluster_setup.html) and [Mesos](../deployment/mesos.html) deployments
* Total process memory ([`jobmanager.memory.process.size`](../config.html#jobmanager-memory-process-size)) for containerized deployments ([Kubernetes](../deployment/kubernetes.html) and [Yarn](../deployment/yarn_setup.html))
+
It is also recommended using these new options instead of the legacy ones as they might be completely removed in the following releases.
-Now, if only the *total Flink memory* or *total process memory* is configured, then the [JVM Heap](mem_setup_master.html#configure-jvm-heap)
+Now, if only the *total Flink memory* or *total process memory* is configured, then the [JVM Heap]({% link ops/memory/mem_setup_jobmanager.md %}#configure-jvm-heap)
is also derived as the rest of what is left after subtracting all other components from the total memory, see also
[how to configure total memory](mem_setup.html#configure-total-memory). Additionally, you can now have more direct
-control over the [JVM Heap](mem_setup_master.html#configure-jvm-heap) by adjusting the
+control over the [JVM Heap]({% link ops/memory/mem_setup_jobmanager.md %}#configure-jvm-heap) by adjusting the
[`jobmanager.memory.heap.size`](../config.html#jobmanager-memory-heap-size) option.
+## Flink JVM process memory limits
+
+Since *1.10* release, Flink sets the *JVM Metaspace* and *JVM Direct Memory* limits for the TaskManager process
+by adding the corresponding JVM arguments. Since *1.11* release, Flink also sets the *JVM Metaspace* limit for the JobManager process.
+You can enable the *JVM Direct Memory* limit for JobManager process if you set the
+[`jobmanager.memory.enable-jvm-direct-memory-limit`](../config.html#jobmanager-memory-enable-jvm-direct-memory-limit) option.
+See also [JVM parameters](mem_setup.html#jvm-parameters).
+
+Flink sets the mentioned JVM memory limits to simplify debugging of the corresponding memory leaks and avoid
+[the container out-of-memory errors](mem_trouble.html#container-memory-exceeded).
+See also the troubleshooting guide for details about the [JVM Metaspace](mem_trouble.html#outofmemoryerror-metaspace)
+and [JVM Direct Memory](mem_trouble.html#outofmemoryerror-direct-buffer-memory) *OutOfMemoryErrors*.
+
## Container Cut-Off Memory
For containerized deployments, you could previously specify a cut-off memory. This memory could accommodate for unaccounted memory allocations.
@@ -259,12 +273,12 @@ The other direct or native off-heap memory consumers can now be addressed by the
* JVM metaspace ([`taskmanager.memory.jvm-metaspace.size`](../config.html#taskmanager-memory-jvm-metaspace-size))
* [JVM overhead](mem_setup_tm.html#detailed-memory-model)
-### for Masters
+### for JobManagers
The direct or native off-heap memory consumers can now be addressed by the following new configuration options:
* Off-heap memory ([`jobmanager.memory.off-heap.size`](../config.html#jobmanager-memory-off-heap-size))
* JVM metaspace ([`jobmanager.memory.jvm-metaspace.size`](../config.html#jobmanager-memory-jvm-metaspace-size))
-* [JVM overhead](mem_setup_master.html#detailed-configuration)
+* [JVM overhead]({% link ops/memory/mem_setup_jobmanager.md %}#detailed-configuration)
## Default Configuration in flink-conf.yaml
@@ -273,7 +287,7 @@ This section describes the changes of the default `flink-conf.yaml` shipped with
The total memory for TaskManagers (`taskmanager.heap.size`) is replaced by [`taskmanager.memory.process.size`](../config.html#taskmanager-memory-process-size)
in the default `flink-conf.yaml`. The value increased from 1024Mb to 1728Mb.
-The total memory for Masters (`jobmanager.heap.size`) is replaced by [`jobmanager.memory.process.size`](../config.html#jobmanager-memory-process-size)
+The total memory for JobManagers (`jobmanager.heap.size`) is replaced by [`jobmanager.memory.process.size`](../config.html#jobmanager-memory-process-size)
in the default `flink-conf.yaml`. The value increased from 1024Mb to 1600Mb.
See also [how to configure total memory now](mem_setup.html#configure-total-memory).
diff --git a/docs/ops/memory/mem_migration.zh.md b/docs/ops/memory/mem_migration.zh.md
index 7c8a525e330c45..68d8e6334a7d39 100644
--- a/docs/ops/memory/mem_migration.zh.md
+++ b/docs/ops/memory/mem_migration.zh.md
@@ -22,9 +22,10 @@ specific language governing permissions and limitations
under the License.
-->
-在 1.10 版本中,Flink 的 [TaskExecutor 内存配置方法](mem_setup.html)发生了较大的变化。
+在 *1.10* 和 *1.11* 版本中,Flink 分别对 [TaskManager](mem_setup_tm.html) 和 [JobManager](mem_setup_jobmanager.html) 的内存配置方法做出了较大的改变。
部分配置参数被移除了,或是语义上发生了变化。
-本篇升级指南将介绍如何将 [*Flink 1.9 及以前版本*](https://ci.apache.org/projects/flink/flink-docs-release-1.9/ops/mem_setup.html)的内存配置升级到 *Flink 1.10 及以后版本*。
+本篇升级指南将介绍如何将 [*Flink 1.9 及以前版本*](https://ci.apache.org/projects/flink/flink-docs-release-1.9/ops/mem_setup.html)的 TaskManager 内存配置升级到 *Flink 1.10 及以后版本*,
+以及如何将 *Flink 1.10 及以前版本*的 JobManager 内存配置升级到 *Flink 1.11 及以后版本*。
* toc
{:toc}
@@ -32,20 +33,32 @@ under the License.
注意: 使用新的默认 `flink-conf.yaml` 可能会造成各内存部分的大小发生变化,从而产生性能变化。
diff --git a/docs/ops/memory/mem_setup.md b/docs/ops/memory/mem_setup.md
index 9bf24b67f2037b..98ca61da62d916 100644
--- a/docs/ops/memory/mem_setup.md
+++ b/docs/ops/memory/mem_setup.md
@@ -31,7 +31,7 @@ Flink allows both high level and fine-grained tuning of memory allocation within
{:toc}
The further described memory configuration is applicable starting with the release version *1.10* for TaskManager and
-*1.11* for Master processes. If you upgrade Flink from earlier versions, check the [migration guide](mem_migration.html)
+*1.11* for JobManager processes. If you upgrade Flink from earlier versions, check the [migration guide](mem_migration.html)
because many changes were introduced with the *1.10* and *1.11* releases.
## Configure Total Memory
@@ -47,17 +47,17 @@ and by the JVM to run the process. The *total Flink memory* consumption includes
The simplest way to setup memory in Flink is to configure either of the two following options:
-| **Component** | **Option for TaskManager** | **Option for Master** |
+| **Component** | **Option for TaskManager** | **Option for JobManager** |
| :------------------------------------ | :---------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------- |
| Total Flink memory | [`taskmanager.memory.flink.size`](../config.html#taskmanager-memory-flink-size) | [`jobmanager.memory.flink.size`](../config.html#jobmanager-memory-flink-size) |
| Total process memory | [`taskmanager.memory.process.size`](../config.html#taskmanager-memory-process-size) | [`jobmanager.memory.process.size`](../config.html#jobmanager-memory-process-size) |
{:.table-bordered}
-Note For local execution, see detailed information for [TaskManager](mem_setup_tm.html#local-execution) and [Master](mem_setup_master.html#local-execxution) processes.
+Note For local execution, see detailed information for [TaskManager](mem_setup_tm.html#local-execution) and [JobManager]({% link ops/memory/mem_setup_jobmanager.md %}#local-execution) processes.
The rest of the memory components will be adjusted automatically, based on default values or additionally configured options.
-See also how to set up other components for [TaskManager](mem_setup_tm.html) and [Master](mem_setup_master.html) memory.
+See also how to set up other components for [TaskManager](mem_setup_tm.html) and [JobManager]({% link ops/memory/mem_setup_jobmanager.md %}) memory.
Configuring *total Flink memory* is better suited for [standalone deployments](../deployment/cluster_setup.html)
where you want to declare how much memory is given to Flink itself. The *total Flink memory* splits up into *JVM Heap*
@@ -71,13 +71,13 @@ For the containerized deployments it corresponds to the size of the requested co
Another way to set up the memory is to configure the required internal components of the *total Flink memory* which are
specific to the concrete Flink process. Check how to configure them for [TaskManager](mem_setup_tm.html#configure-heap-and-managed-memory)
-and for [Master](mem_setup_master.html#configure-jvm-heap).
+and for [JobManager]({% link ops/memory/mem_setup_jobmanager.md %}#configure-jvm-heap).
Note One of the three mentioned ways has to be used to configure Flink’s memory
(except for local execution), or the Flink startup will fail. This means that one of the following option subsets,
which do not have default values, have to be configured explicitly:
-| **for TaskManager:** | **for Master:** |
+| **for TaskManager:** | **for JobManager:** |
| :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :-------------------------------------------------------------------------------- |
| [`taskmanager.memory.flink.size`](../config.html#taskmanager-memory-flink-size) | [`jobmanager.memory.flink.size`](../config.html#jobmanager-memory-flink-size) |
| [`taskmanager.memory.process.size`](../config.html#taskmanager-memory-process-size) | [`jobmanager.memory.process.size`](../config.html#jobmanager-memory-process-size) |
@@ -94,17 +94,20 @@ Configuring other memory components also requires caution as it can produce furt
Flink explicitly adds the following memory related JVM arguments while starting its processes, based on the configured
or derived memory component sizes:
-| **JVM Arguments** | **Value for TaskManager** | **Value for Master** |
-| :---------------------------------------- | :------------------------------------------------- | :------------------------------------------------ |
-| *-Xmx* and *-Xms* | Framework + Task Heap Memory | JVM Heap Memory |
-| *-XX:MaxDirectMemorySize* | Framework + Task Off-heap (*) + Network Memory | Off-heap Memory (*) |
-| *-XX:MaxMetaspaceSize* | JVM Metaspace | JVM Metaspace |
+| **JVM Arguments** | **Value for TaskManager** | **Value for JobManager** |
+| :------------------------------------------------------------------------------------- | :------------------------------------------------- | :------------------------------------------------ |
+| *-Xmx* and *-Xms* | Framework + Task Heap Memory | JVM Heap Memory |
+| *-XX:MaxDirectMemorySize* (always added only for TaskManager, see note for JobManager) | Framework + Task Off-heap (\*) + Network Memory | Off-heap Memory (\*),(\*\*) |
+| *-XX:MaxMetaspaceSize* | JVM Metaspace | JVM Metaspace |
{:.table-bordered}
-(*) Notice, that the native non-direct usage of memory in user code can be also accounted for as a part of the off-heap memory.
+(\*) Notice, that the native non-direct usage of memory in user code can be also accounted for as a part of the off-heap memory.
+
+(\*\*) The *JVM Direct memory limit* is added for JobManager process only if the corresponding option
+[`jobmanager.memory.enable-jvm-direct-memory-limit`](../config.html#jobmanager-memory-enable-jvm-direct-memory-limit) is set.
Check also the detailed memory model for [TaskManager](mem_setup_tm.html#detailed-memory-model) and
-[Master](mem_setup_master.html#detailed-configuration) to understand how to configure the relevant components.
+[JobManager]({% link ops/memory/mem_setup_jobmanager.md %}#detailed-configuration) to understand how to configure the relevant components.
## Capped Fractionated Components
@@ -114,7 +117,7 @@ This section describes the configuration details of options which can be a fract
* *Network memory* can be a fraction of the *total Flink memory* (only for TaskManager)
Check also the detailed memory model for [TaskManager](mem_setup_tm.html#detailed-memory-model) and
-[Master](mem_setup_master.html#detailed-configuration) to understand how to configure the relevant components.
+[JobManager]({% link ops/memory/mem_setup_jobmanager.md %}#detailed-configuration) to understand how to configure the relevant components.
The size of those components always has to be between its maximum and minimum value, otherwise Flink startup will fail.
The maximum and minimum values have defaults or can be explicitly set by corresponding configuration options.
@@ -142,11 +145,11 @@ It can also happen that the fraction is ignored if the sizes of the total memory
In this case, the *JVM Overhead* is the rest of the total memory. The derived value still has to be within its min/max
range otherwise the configuration fails. For example, suppose only the following memory options are set:
- *total Process memory* = 1000Mb,
-- *task heap* = 100Mb, (similar example can be for *JVM Heap* in Flink Master)
+- *task heap* = 100Mb, (similar example can be for *JVM Heap* in the JobManager)
- *JVM Overhead min* = 64Mb,
- *JVM Overhead max* = 256Mb,
- *JVM Overhead fraction* = 0.1
All other components of the *total Process memory* have default values, including the default *Managed Memory* fraction
-(or *Off-heap* memory in Flink Master). Then the *JVM Overhead* is not the fraction (1000Mb x 0.1 = 100Mb), but the rest
+(or *Off-heap* memory in the JobManager). Then the *JVM Overhead* is not the fraction (1000Mb x 0.1 = 100Mb), but the rest
of the *total Process memory* which will either be within the range 64-256Mb or fail.
diff --git a/docs/ops/memory/mem_setup.zh.md b/docs/ops/memory/mem_setup.zh.md
index 9bf24b67f2037b..958bcf0a0f97a2 100644
--- a/docs/ops/memory/mem_setup.zh.md
+++ b/docs/ops/memory/mem_setup.zh.md
@@ -1,5 +1,5 @@
---
-title: "Set up Flink's Process Memory"
+title: "配置 Flink 进程的内存"
nav-parent_id: ops_mem
nav-pos: 1
---
@@ -22,131 +22,133 @@ specific language governing permissions and limitations
under the License.
-->
-Apache Flink provides efficient workloads on top of the JVM by tightly controlling the memory usage of its various components.
-While the community strives to offer sensible defaults to all configurations, the full breadth of applications
-that users deploy on Flink means this isn't always possible. To provide the most production value to our users,
-Flink allows both high level and fine-grained tuning of memory allocation within clusters.
+Apache Flink 基于 JVM 的高效处理能力,依赖于其对各组件内存用量的细致掌控。
+考虑到用户在 Flink 上运行的应用的多样性,尽管社区已经努力为所有配置项提供合理的默认值,仍无法满足所有情况下的需求。
+为了给用户生产提供最大化的价值, Flink 允许用户在整体上以及细粒度上对集群的内存分配进行调整。
* toc
{:toc}
-The further described memory configuration is applicable starting with the release version *1.10* for TaskManager and
-*1.11* for Master processes. If you upgrade Flink from earlier versions, check the [migration guide](mem_migration.html)
-because many changes were introduced with the *1.10* and *1.11* releases.
+本文接下来介绍的内存配置方法适用于 *1.10* 及以上版本的 TaskManager 进程和 *1.11* 及以上版本的 JobManager 进程。
+Flink 在 *1.10* 和 *1.11* 版本中对内存配置部分进行了较大幅度的改动,从早期版本升级的用户请参考[升级指南](mem_migration.html)。
-## Configure Total Memory
+
-The *total process memory* of Flink JVM processes consists of memory consumed by the Flink application (*total Flink memory*)
-and by the JVM to run the process. The *total Flink memory* consumption includes usage of *JVM Heap* and *Off-heap*
-(*Direct* or *Native*) memory.
+## 配置总内存
+
+Flink JVM 进程的*进程总内存(Total Process Memory)*包含了由 Flink 应用使用的内存(*Flink 总内存*)以及由运行 Flink 的 JVM 使用的内存。
+*Flink 总内存(Total Flink Memory)*包括 *JVM 堆内存(Heap Memory)*和*堆外内存(Off-Heap Memory)*。
+其中堆外内存包括*直接内存(Direct Memory)*和*本地内存(Native Memory)*。
-The simplest way to setup memory in Flink is to configure either of the two following options:
+配置 Flink 进程内存最简单的方法是指定以下两个配置项中的任意一个:
-| **Component** | **Option for TaskManager** | **Option for Master** |
+| **配置项** | **TaskManager 配置参数** | **JobManager 配置参数** |
| :------------------------------------ | :---------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------- |
-| Total Flink memory | [`taskmanager.memory.flink.size`](../config.html#taskmanager-memory-flink-size) | [`jobmanager.memory.flink.size`](../config.html#jobmanager-memory-flink-size) |
-| Total process memory | [`taskmanager.memory.process.size`](../config.html#taskmanager-memory-process-size) | [`jobmanager.memory.process.size`](../config.html#jobmanager-memory-process-size) |
+| Flink 总内存 | [`taskmanager.memory.flink.size`](../config.html#taskmanager-memory-flink-size) | [`jobmanager.memory.flink.size`](../config.html#jobmanager-memory-flink-size) |
+| 进程总内存 | [`taskmanager.memory.process.size`](../config.html#taskmanager-memory-process-size) | [`jobmanager.memory.process.size`](../config.html#jobmanager-memory-process-size) |
{:.table-bordered}
-Note For local execution, see detailed information for [TaskManager](mem_setup_tm.html#local-execution) and [Master](mem_setup_master.html#local-execxution) processes.
+提示
+关于本地执行,请分别参考 [TaskManager](mem_setup_tm.html#local-execution) 和 [JobManager](mem_setup_jobmanager.html#local-execution) 的相关文档。
-The rest of the memory components will be adjusted automatically, based on default values or additionally configured options.
-See also how to set up other components for [TaskManager](mem_setup_tm.html) and [Master](mem_setup_master.html) memory.
+Flink 会根据默认值或其他配置参数自动调整剩余内存部分的大小。
+关于各内存部分的更多细节,请分别参考 [TaskManager](mem_setup_tm.html) 和 [JobManager](mem_setup_jobmanager.html) 的相关文档。
-Configuring *total Flink memory* is better suited for [standalone deployments](../deployment/cluster_setup.html)
-where you want to declare how much memory is given to Flink itself. The *total Flink memory* splits up into *JVM Heap*
-and *Off-heap* memory.
-See also [how to configure memory for standalone deployments](mem_tuning.html#configure-memory-for-standalone-deployment).
+对于[独立部署模式(Standalone Deployment)](../deployment/cluster_setup.html),如果你希望指定由 Flink 应用本身使用的内存大小,最好选择配置 *Flink 总内存*。
+*Flink 总内存*会进一步划分为 *JVM 堆内存*和*堆外内存*。
+更多详情请参考[如何为独立部署模式配置内存](mem_tuning.html#configure-memory-for-standalone-deployment)。
-If you configure *total process memory* you declare how much memory in total should be assigned to the Flink *JVM process*.
-For the containerized deployments it corresponds to the size of the requested container, see also
-[how to configure memory for containers](mem_tuning.html#configure-memory-for-containers)
-([Kubernetes](../deployment/kubernetes.html), [Yarn](../deployment/yarn_setup.html) or [Mesos](../deployment/mesos.html)).
+通过配置*进程总内存*可以指定由 Flink *JVM 进程*使用的总内存大小。
+对于容器化部署模式(Containerized Deployment),这相当于申请的容器(Container)大小,详情请参考[如何配置容器内存](mem_tuning.html#configure-memory-for-containers)([Kubernetes](../deployment/kubernetes.html)、[Yarn](../deployment/yarn_setup.html) 或 [Mesos](../deployment/mesos.html))。
-Another way to set up the memory is to configure the required internal components of the *total Flink memory* which are
-specific to the concrete Flink process. Check how to configure them for [TaskManager](mem_setup_tm.html#configure-heap-and-managed-memory)
-and for [Master](mem_setup_master.html#configure-jvm-heap).
+此外,还可以通过设置 *Flink 总内存*的特定内部组成部分的方式来进行内存配置。
+不同进程需要设置的内存组成部分是不一样的。
+详情请分别参考 [TaskManager](mem_setup_tm.html#configure-heap-and-managed-memory) 和 [JobManager](mem_setup_jobmanager.html#configure-jvm-heap) 的相关文档。
-Note One of the three mentioned ways has to be used to configure Flink’s memory
-(except for local execution), or the Flink startup will fail. This means that one of the following option subsets,
-which do not have default values, have to be configured explicitly:
+提示
+以上三种方式中,用户需要至少选择其中一种进行配置(本地运行除外),否则 Flink 将无法启动。
+这意味着,用户需要从以下无默认值的配置参数(或参数组合)中选择一个给出明确的配置:
-| **for TaskManager:** | **for Master:** |
+| **TaskManager:** | **JobManager:** |
| :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :-------------------------------------------------------------------------------- |
| [`taskmanager.memory.flink.size`](../config.html#taskmanager-memory-flink-size) | [`jobmanager.memory.flink.size`](../config.html#jobmanager-memory-flink-size) |
| [`taskmanager.memory.process.size`](../config.html#taskmanager-memory-process-size) | [`jobmanager.memory.process.size`](../config.html#jobmanager-memory-process-size) |
-| [`taskmanager.memory.task.heap.size`](../config.html#taskmanager-memory-task-heap-size) and [`taskmanager.memory.managed.size`](../config.html#taskmanager-memory-managed-size) | [`jobmanager.memory.heap.size`](../config.html#jobmanager-memory-heap-size) |
+| [`taskmanager.memory.task.heap.size`](../config.html#taskmanager-memory-task-heap-size) 和 [`taskmanager.memory.managed.size`](../config.html#taskmanager-memory-managed-size) | [`jobmanager.memory.heap.size`](../config.html#jobmanager-memory-heap-size) |
{:.table-bordered}
-Note Explicitly configuring both *total process memory* and *total Flink memory*
-is not recommended. It may lead to deployment failures due to potential memory configuration conflicts.
-Configuring other memory components also requires caution as it can produce further configuration conflicts.
+提示
+不建议同时设置*进程总内存*和 *Flink 总内存*。
+这可能会造成内存配置冲突,从而导致部署失败。
+额外配置其他内存部分时,同样需要注意可能产生的配置冲突。
+
+
-## JVM Parameters
+## JVM 参数
-Flink explicitly adds the following memory related JVM arguments while starting its processes, based on the configured
-or derived memory component sizes:
+Flink 进程启动时,会根据配置的和自动推导出的各内存部分大小,显式地设置以下 JVM 参数:
-| **JVM Arguments** | **Value for TaskManager** | **Value for Master** |
+| **JVM 参数** | **TaskManager 取值** | **JobManager 取值** |
| :---------------------------------------- | :------------------------------------------------- | :------------------------------------------------ |
-| *-Xmx* and *-Xms* | Framework + Task Heap Memory | JVM Heap Memory |
-| *-XX:MaxDirectMemorySize* | Framework + Task Off-heap (*) + Network Memory | Off-heap Memory (*) |
+| *-Xmx* 和 *-Xms* | 框架堆内存 + 任务堆内存 | JVM 堆内存 |
+| *-XX:MaxDirectMemorySize* (TaskManager 始终设置,JobManager 见注释) | 框架堆外内存 + 任务堆外内存(\*) + 网络内存 | 堆外内存 (\*) (\*\*) |
| *-XX:MaxMetaspaceSize* | JVM Metaspace | JVM Metaspace |
{:.table-bordered}
-(*) Notice, that the native non-direct usage of memory in user code can be also accounted for as a part of the off-heap memory.
+(\*) 请注意,堆外内存也包括了用户代码使用的本地内存(非直接内存)。
+
+(\*\*) 只有在 [`jobmanager.memory.enable-jvm-direct-memory-limit`](../config.html#jobmanager-memory-enable-jvm-direct-memory-limit) 设置为 `true` 时,JobManager 才会设置 *JVM 直接内存限制*。
-Check also the detailed memory model for [TaskManager](mem_setup_tm.html#detailed-memory-model) and
-[Master](mem_setup_master.html#detailed-configuration) to understand how to configure the relevant components.
+相关内存部分的配置方法,请同时参考 [TaskManager](mem_setup_tm.html#detailed-memory-model) 和 [JobManager](mem_setup_jobmanager.html#detailed-configuration) 的详细内存模型。
-## Capped Fractionated Components
+
-This section describes the configuration details of options which can be a fraction of some other memory size while being constrained by a min-max range:
+## 受限的等比内存部分
-* *JVM Overhead* can be a fraction of the *total process memory*
-* *Network memory* can be a fraction of the *total Flink memory* (only for TaskManager)
+本节介绍下列内存部分的配置方法,它们都可以通过指定在总内存中所占比例的方式进行配置,同时受限于相应的的最大/最小值范围。
+* *JVM 开销*:可以配置占用*进程总内存*的固定比例
+* *网络内存*:可以配置占用 *Flink 总内存*的固定比例(仅针对 TaskManager)
-Check also the detailed memory model for [TaskManager](mem_setup_tm.html#detailed-memory-model) and
-[Master](mem_setup_master.html#detailed-configuration) to understand how to configure the relevant components.
+相关内存部分的配置方法,请同时参考 [TaskManager](mem_setup_tm.html#detailed-memory-model) 和 [JobManager](mem_setup_jobmanager.html#detailed-configuration) 的详细内存模型。
-The size of those components always has to be between its maximum and minimum value, otherwise Flink startup will fail.
-The maximum and minimum values have defaults or can be explicitly set by corresponding configuration options.
-For example, if you only set the following memory options:
-- *total Process memory* = 1000Mb,
-- *JVM Overhead min* = 64Mb,
-- *JVM Overhead max* = 128Mb,
-- *JVM Overhead fraction* = 0.1
+这些内存部分的大小必须在相应的最大值、最小值范围内,否则 Flink 将无法启动。
+最大值、最小值具有默认值,也可以通过相应的配置参数进行设置。
+例如,如果仅配置下列参数:
+- *进程总内存* = 1000Mb
+- *JVM 开销最小值* = 64Mb
+- *JVM 开销最大值* = 128Mb
+- *JVM 开销占比* = 0.1
-then the *JVM Overhead* will be 1000Mb x 0.1 = 100Mb which is within the range 64-128Mb.
+那么 *JVM 开销*的实际大小将会是 1000Mb x 0.1 = 100Mb,在 64-128Mb 的范围内。
-Notice if you configure the same maximum and minimum value it effectively fixes the size to that value.
+如果将最大值、最小值设置成相同大小,那相当于明确指定了该内存部分的大小。
-If you do not explicitly configure the component memory, then Flink will use the fraction to calculate the memory size
-based on the total memory. The calculated value is capped by its corresponding min/max options.
-For example, if only the following memory options are set:
-- *total Process memory* = 1000Mb,
-- *JVM Overhead min* = 128Mb,
-- *JVM Overhead max* = 256Mb,
-- *JVM Overhead fraction* = 0.1
+如果没有明确指定内存部分的大小,Flink 会根据总内存和占比计算出该内存部分的大小。
+计算得到的内存大小将受限于相应的最大值、最小值范围。
+例如,如果仅配置下列参数:
+- *进程总内存* = 1000Mb
+- *JVM 开销最小值* = 128Mb
+- *JVM 开销最大值* = 256Mb
+- *JVM 开销占比* = 0.1
-then the *JVM Overhead* will be 128Mb because the size derived from fraction is 100Mb, and it is less than the minimum.
+那么 *JVM 开销*的实际大小将会是 128Mb,因为根据总内存和占比计算得到的内存大小 100Mb 小于最小值。
-It can also happen that the fraction is ignored if the sizes of the total memory and its other components are defined.
-In this case, the *JVM Overhead* is the rest of the total memory. The derived value still has to be within its min/max
-range otherwise the configuration fails. For example, suppose only the following memory options are set:
-- *total Process memory* = 1000Mb,
-- *task heap* = 100Mb, (similar example can be for *JVM Heap* in Flink Master)
-- *JVM Overhead min* = 64Mb,
-- *JVM Overhead max* = 256Mb,
-- *JVM Overhead fraction* = 0.1
+如果配置了总内存和其他内存部分的大小,那么 Flink 也有可能会忽略给定的占比。
+这种情况下,受限的等比内存部分的实际大小是总内存减去其他所有内存部分后剩余的部分。
+这样推导得出的内存大小必须符合最大值、最小值范围,否则 Flink 将无法启动。
+例如,如果仅配置下列参数:
+- *进程总内存* = 1000Mb
+- *任务堆内存* = 100Mb(或 JobManager 的 *JVM 堆内存*)
+- *JVM 开销最小值* = 64Mb
+- *JVM 开销最大值* = 256Mb
+- *JVM 开销占比* = 0.1
-All other components of the *total Process memory* have default values, including the default *Managed Memory* fraction
-(or *Off-heap* memory in Flink Master). Then the *JVM Overhead* is not the fraction (1000Mb x 0.1 = 100Mb), but the rest
-of the *total Process memory* which will either be within the range 64-256Mb or fail.
+*进程总内存*中所有其他内存部分均有默认大小,包括 TaskManager 的*托管内存*默认占比或 JobManager 的默认*堆外内存*。
+因此,*JVM 开销*的实际大小不是根据占比算出的大小(1000Mb x 0.1 = 100Mb),而是*进程总内存*中剩余的部分。
+这个剩余部分的大小必须在 64-256Mb 的范围内,否则将会启动失败。
\ No newline at end of file
diff --git a/docs/ops/memory/mem_setup_jobmanager.md b/docs/ops/memory/mem_setup_jobmanager.md
new file mode 100644
index 00000000000000..379e2f78eda73f
--- /dev/null
+++ b/docs/ops/memory/mem_setup_jobmanager.md
@@ -0,0 +1,102 @@
+---
+title: "Set up JobManager Memory"
+nav-parent_id: ops_mem
+nav-pos: 3
+---
+
+
+The JobManager is the controlling element of the Flink Cluster.
+It consists of three distinct components: Resource Manager, Dispatcher and one JobMaster per running Flink Job.
+This guide walks you through high level and fine-grained memory configurations for the JobManager.
+
+* toc
+{:toc}
+
+The further described memory configuration is applicable starting with the release version *1.11*. If you upgrade Flink
+from earlier versions, check the [migration guide](mem_migration.html) because many changes were introduced with the *1.11* release.
+
+Note This memory setup guide is relevant only for the JobManager!
+The JobManager memory components have a similar but simpler structure compared to the [TaskManagers' memory configuration](mem_setup_tm.html).
+
+## Configure Total Memory
+
+The simplest way to set up the memory configuration is to configure the [total memory](mem_setup.html#configure-total-memory) for the process.
+If you run the JobManager process using local [execution mode](#local-execution) you do not need to configure memory options, they will have no effect.
+
+## Detailed configuration
+
+
+
+
+
+
+The following table lists all memory components, depicted above, and references Flink configuration options which
+affect the size of the respective components:
+
+| **Component** | **Configuration options** | **Description** |
+| :------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| [JVM Heap](#configure-jvm-heap) | [`jobmanager.memory.heap.size`](../config.html#jobmanager-memory-heap-size) | *JVM Heap* memory size for job manager. |
+| [Off-heap Memory](#configure-off-heap-memory) | [`jobmanager.memory.off-heap.size`](../config.html#jobmanager-memory-off-heap-size) | *Off-heap* memory size for job manager. This option covers all off-heap memory usage including direct and native memory allocation. |
+| [JVM metaspace](mem_setup.html#jvm-parameters) | [`jobmanager.memory.jvm-metaspace.size`](../config.html#jobmanager-memory-jvm-metaspace-size) | Metaspace size of the Flink JVM process |
+| JVM Overhead | [`jobmanager.memory.jvm-overhead.min`](../config.html#jobmanager-memory-jvm-overhead-min) [`jobmanager.memory.jvm-overhead.max`](../config.html#jobmanager-memory-jvm-overhead-max) [`jobmanager.memory.jvm-overhead.fraction`](../config.html#jobmanager-memory-jvm-overhead-fraction) | Native memory reserved for other JVM overhead: e.g. thread stacks, code cache, garbage collection space etc, it is a [capped fractionated component](mem_setup.html#capped-fractionated-components) of the [total process memory](mem_setup.html#configure-total-memory) |
+{:.table-bordered}
+
+
+### Configure JVM Heap
+
+As mentioned before in the [total memory description](mem_setup.html#configure-total-memory), another way to set up the memory
+for the JobManager is to specify explicitly the *JVM Heap* size ([`jobmanager.memory.heap.size`](../config.html#jobmanager-memory-heap-size)).
+It gives more control over the available *JVM Heap* which is used by:
+
+* Flink framework
+* User code executed during job submission (e.g. for certain batch sources) or in checkpoint completion callbacks
+
+The required size of *JVM Heap* is mostly driven by the number of running jobs, their structure, and requirements for
+the mentioned user code.
+
+Note If you have configured the *JVM Heap* explicitly, it is recommended to set
+neither *total process memory* nor *total Flink memory*. Otherwise, it may easily lead to memory configuration conflicts.
+
+The Flink scripts and CLI set the *JVM Heap* size via the JVM parameters *-Xms* and *-Xmx* when they start the JobManager process, see also [JVM parameters](mem_setup.html#jvm-parameters).
+
+### Configure Off-heap Memory
+
+The *Off-heap* memory component accounts for any type of *JVM direct memory* and *native memory* usage. Therefore,
+you can also enable the *JVM Direct Memory* limit by setting the [`jobmanager.memory.enable-jvm-direct-memory-limit`](../config.html#jobmanager-memory-enable-jvm-direct-memory-limit) option.
+If this option is configured, Flink will set the limit to the *Off-heap* memory size via the corresponding JVM argument: *-XX:MaxDirectMemorySize*.
+See also [JVM parameters](mem_setup.html#jvm-parameters).
+
+The size of this component can be configured by [`jobmanager.memory.off-heap.size`](../config.html#jobmanager-memory-off-heap-size)
+option. This option can be tuned e.g. if the JobManager process throws ‘OutOfMemoryError: Direct buffer memory’, see
+[the troubleshooting guide](mem_trouble.html#outofmemoryerror-direct-buffer-memory) for more information.
+
+There can be the following possible sources of *Off-heap* memory consumption:
+
+* Flink framework dependencies (e.g. Akka network communication)
+* User code executed during job submission (e.g. for certain batch sources) or in checkpoint completion callbacks
+
+Note If you have configured the [Total Flink Memory](mem_setup.html#configure-total-memory)
+and the [JVM Heap](#configure-jvm-heap) explicitly but you have not configured the *Off-heap* memory, the size of the *Off-heap* memory
+will be derived as the [Total Flink Memory](mem_setup.html#configure-total-memory) minus the [JVM Heap](#configure-jvm-heap).
+The default value of the *Off-heap* memory option will be ignored.
+
+## Local Execution
+
+If you run Flink locally (e.g. from your IDE) without creating a cluster, then the JobManager memory configuration options are ignored.
diff --git a/docs/ops/memory/mem_setup_jobmanager.zh.md b/docs/ops/memory/mem_setup_jobmanager.zh.md
new file mode 100644
index 00000000000000..455ac232a9623b
--- /dev/null
+++ b/docs/ops/memory/mem_setup_jobmanager.zh.md
@@ -0,0 +1,108 @@
+---
+title: "配置 JobManager 内存"
+nav-parent_id: ops_mem
+nav-pos: 3
+---
+
+
+JobManager 是 Flink 集群的控制单元。
+它由三种不同的组件组成:ResourceManager、Dispatcher 和每个正在运行作业的 JobMaster。
+本篇文档将介绍 JobManager 内存在整体上以及细粒度上的配置方法。
+
+* toc
+{:toc}
+
+本文接下来介绍的内存配置方法适用于 *1.11* 及以上版本。
+Flink 在 *1.11* 版本中对内存配置部分进行了较大幅度的改动,从早期版本升级的用户请参考[升级指南](mem_migration.html)。
+
+提示
+本篇内存配置文档仅针对 JobManager!
+与 [TaskManager](mem_setup_tm.html) 相比,JobManager 具有相似但更加简单的内存模型。
+
+
+
+## 配置总内存
+
+配置 JobManager 内存最简单的方法就是进程的[配置总内存](mem_setup.html#configure-total-memory)。
+[本地执行模式](#local-execution)下不需要为 JobManager 进行内存配置,配置参数将不会生效。
+
+
+
+## 详细配置
+
+
+
+
+
+
+如上图所示,下表中列出了 Flink JobManager 内存模型的所有组成部分,以及影响其大小的相关配置参数。
+
+| **组成部分** | **配置参数** | **描述** |
+| :------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| [JVM 堆内存](#configure-jvm-heap) | [`jobmanager.memory.heap.size`](../config.html#jobmanager-memory-heap-size) | JobManager 的 *JVM 堆内存*。 |
+| [堆外内存](#configure-off-heap-memory) | [`jobmanager.memory.off-heap.size`](../config.html#jobmanager-memory-off-heap-size) | JobManager 的*堆外内存(直接内存或本地内存)*。 |
+| [JVM Metaspace](mem_setup.html#jvm-parameters) | [`jobmanager.memory.jvm-metaspace.size`](../config.html#jobmanager-memory-jvm-metaspace-size) | Flink JVM 进程的 Metaspace。 |
+| JVM 开销 | [`jobmanager.memory.jvm-overhead.min`](../config.html#jobmanager-memory-jvm-overhead-min) [`jobmanager.memory.jvm-overhead.max`](../config.html#jobmanager-memory-jvm-overhead-max) [`jobmanager.memory.jvm-overhead.fraction`](../config.html#jobmanager-memory-jvm-overhead-fraction) | 用于其他 JVM 开销的本地内存,例如栈空间、垃圾回收空间等。该内存部分为基于[进程总内存](mem_setup.html#configure-total-memory)的[受限的等比内存部分](mem_setup.html#capped-fractionated-components)。 |
+{:.table-bordered}
+
+
+
+
+### 配置 JVM 堆内存
+
+如[配置总内存](mem_setup.html#configure-total-memory)中所述,另一种配置 JobManager 内存的方式是明确指定 *JVM 堆内存*的大小([`jobmanager.memory.heap.size`](../config.html#jobmanager-memory-heap-size))。
+通过这种方式,用户可以更好地掌控用于以下用途的 *JVM 堆内存*大小。
+* Flink 框架
+* 在作业提交时(例如一些特殊的批处理 Source)及 Checkpoint 完成的回调函数中执行的用户代码
+
+Flink 需要多少 *JVM 堆内存*,很大程度上取决于运行的作业数量、作业的结构及上述用户代码的需求。
+
+提示
+如果已经明确设置了 *JVM 堆内存*,建议不要再设置*进程总内存*或 *Flink 总内存*,否则可能会造成内存配置冲突。
+
+在启动 JobManager 进程时,Flink 启动脚本及客户端通过设置 JVM 参数 *-Xms* 和 *-Xmx* 来管理 JVM 堆空间的大小。
+请参考 [JVM 参数](mem_setup.html#jvm-parameters)。
+
+
+
+### 配置堆外内存
+
+*堆外内存*包括 *JVM 直接内存* 和 *本地内存*。
+可以通过配置参数 [`jobmanager.memory.enable-jvm-direct-memory-limit`](../config.html#jobmanager-memory-enable-jvm-direct-memory-limit) 设置是否启用 *JVM 直接内存限制*。
+如果该配置项设置为 `true`,Flink 会根据配置的*堆外内存*大小设置 JVM 参数 *-XX:MaxDirectMemorySize*。
+请参考 [JVM 参数](mem_setup.html#jvm-parameters)。
+
+可以通过配置参数 [`jobmanager.memory.off-heap.size`](../config.html#jobmanager-memory-off-heap-size) 设置堆外内存的大小。
+如果遇到 JobManager 进程抛出 “OutOfMemoryError: Direct buffer memory” 的异常,可以尝试调大这项配置。
+请参考[常见问题](mem_trouble.html#outofmemoryerror-direct-buffer-memory)。
+
+一下情况可能用到堆外内存:
+* Flink 框架依赖(例如 Akka 的网络通信)
+* 在作业提交时(例如一些特殊的批处理 Source)及 Checkpoint 完成的回调函数中执行的用户代码
+
+提示
+如果同时配置了 [Flink 总内存](mem_setup.html#configure-total-memory)和 [JVM 堆内存](#configure-jvm-heap),且没有配置*堆外内存*,那么*堆外内存*的大小将会是 [Flink 总内存](mem_setup.html#configure-total-memory)减去[JVM 堆内存](#configure-jvm-heap)。
+这种情况下,*对外内存*的默认大小将不会生效。
+
+
+
+## 本地执行
+
+如果你是在本地运行 Flink(例如在 IDE 中)而非创建一个集群,那么 JobManager 的内存配置将不会生效。
diff --git a/docs/ops/memory/mem_setup_master.md b/docs/ops/memory/mem_setup_master.md
deleted file mode 100644
index dbea142e388eea..00000000000000
--- a/docs/ops/memory/mem_setup_master.md
+++ /dev/null
@@ -1,94 +0,0 @@
----
-title: "Set up Master Memory"
-nav-parent_id: ops_mem
-nav-pos: 3
----
-
-
-The Master is the controlling element of the Flink Cluster.
-It consists of three distinct components: Flink Resource Manager, Flink Dispatcher and one Flink JobManager per running Flink Job.
-This guide walks you through high level and fine-grained memory configurations for the Master.
-
-* toc
-{:toc}
-
-The further described memory configuration is applicable starting with the release version *1.11*. If you upgrade Flink
-from earlier versions, check the [migration guide](mem_migration.html) because many changes were introduced with the *1.11* release.
-
-Note This memory setup guide is relevant only for the Master!
-The Master memory components have a similar but simpler structure compared to the [TaskManagers' memory configuration](mem_setup_tm.html).
-
-## Configure Total Memory
-
-The simplest way to set up the memory configuration is to configure the [total memory](mem_setup.html#configure-total-memory) for the process.
-If you run the Master process using local [execution mode](#local-execution) you do not need to configure memory options, they will have no effect.
-
-## Detailed configuration
-
-The following table lists all memory components, depicted above, and references Flink configuration options which
-affect the size of the respective components:
-
-| **Component** | **Configuration options** | **Description** |
-| :------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| [Total Process Memory](mem_setup.html#configure-total-memory) | [`jobmanager.memory.process.size`](../config.html#jobmanager-memory-process-size) | The *total process memory* size for the job manager. This includes all the memory that a job manager JVM process consumes, consisting of the *total Flink memory*, *JVM metaspace* and *JVM overhead*. |
-| [Total Flink Memory](mem_setup.html#configure-total-memory) | [`jobmanager.memory.flink.size`](../config.html#jobmanager-memory-flink-size) | The *total Flink memory* size for the job manager. This includes all the memory that a job manager consumes, except for *JVM metaspace* and *JVM overhead*. It consists of [JVM Heap](#configure-jvm-heap) and [Off-heap Memory](#configure-off-heap-memory) Memory. |
-| [JVM Heap](#configure-jvm-heap) | [`jobmanager.memory.heap.size`](../config.html#jobmanager-memory-heap-size) | *JVM Heap* memory size for job manager. |
-| [Off-heap Memory](#configure-off-heap-memory) | [`jobmanager.memory.off-heap.size`](../config.html#jobmanager-memory-off-heap-size) | *Off-heap* memory size for job manager. This option covers all off-heap memory usage including direct and native memory allocation. |
-| [JVM metaspace](mem_setup.html#jvm-parameters) | [`jobmanager.memory.jvm-metaspace.size`](../config.html#jobmanager-memory-jvm-metaspace-size) | Metaspace size of the Flink JVM process |
-| JVM Overhead | [`jobmanager.memory.jvm-overhead.min`](../config.html#jobmanager-memory-jvm-overhead-min) [`jobmanager.memory.jvm-overhead.max`](../config.html#jobmanager-memory-jvm-overhead-max) [`jobmanager.memory.jvm-overhead.fraction`](../config.html#jobmanager-memory-jvm-overhead-fraction) | Native memory reserved for other JVM overhead: e.g. thread stacks, code cache, garbage collection space etc, it is a [capped fractionated component](mem_setup.html#capped-fractionated-components) of the [total process memory](mem_setup.html#configure-total-memory) |
-{:.table-bordered}
-
-
-### Configure JVM Heap
-
-As mentioned before in the [total memory description](mem_setup.html#configure-total-memory), another way to set up the memory
-for the Master is to specify explicitly the *JVM Heap* size ([`jobmanager.memory.heap.size`](../config.html#jobmanager-memory-heap-size)).
-It gives more control over the available *JVM Heap* which is used by:
-
-* Flink framework (e.g. *Job cache*)
-* User code executed during job submission (e.g. for certain batch sources) or in checkpoint completion callbacks
-
-The required size of *JVM Heap* is mostly driven by the number of running jobs, their structure, and requirements for
-the mentioned user code.
-
-The *Job cache* resides in the *JVM Heap*. It can be configured by
-[`jobstore.cache-size`](../config.html#jobstore-cache-size) which must be less than the configured or derived *JVM Heap* size.
-
-Note If you have configured the *JVM Heap* explicitly, it is recommended to set
-neither *total process memory* nor *total Flink memory*. Otherwise, it may easily lead to memory configuration conflicts.
-The Flink scripts and CLI set the *JVM Heap* size via the JVM parameters *-Xms* and *-Xmx* when they start the Master process, see also [JVM parameters](mem_setup.html#jvm-parameters).
-
-### Configure Off-heap Memory
-
-The *Off-heap* memory component accounts for any type of *JVM direct memory* and *native memory* usage. Therefore, it
-is also set via the corresponding JVM argument: *-XX:MaxDirectMemorySize*, see also [JVM parameters](mem_setup.html#jvm-parameters).
-
-The size of this component can be configured by [`jobmanager.memory.off-heap.size`](../config.html#jobmanager-memory-off-heap-size)
-option. This option can be tuned e.g. if the Master process throws ‘OutOfMemoryError: Direct buffer memory’, see
-[the troubleshooting guide](mem_trouble.html#outofmemoryerror-direct-buffer-memory) for more information.
-
-There can be the following possible sources of *Off-heap* memory consumption:
-
-* Flink framework dependencies (e.g. Akka network communication)
-* User code executed during job submission (e.g. for certain batch sources) or in checkpoint completion callbacks
-
-## Local Execution
-
-If you run Flink locally (e.g. from your IDE) without creating a cluster, then the Master memory configuration options are ignored.
diff --git a/docs/ops/memory/mem_setup_master.zh.md b/docs/ops/memory/mem_setup_master.zh.md
deleted file mode 100644
index dbea142e388eea..00000000000000
--- a/docs/ops/memory/mem_setup_master.zh.md
+++ /dev/null
@@ -1,94 +0,0 @@
----
-title: "Set up Master Memory"
-nav-parent_id: ops_mem
-nav-pos: 3
----
-
-
-The Master is the controlling element of the Flink Cluster.
-It consists of three distinct components: Flink Resource Manager, Flink Dispatcher and one Flink JobManager per running Flink Job.
-This guide walks you through high level and fine-grained memory configurations for the Master.
-
-* toc
-{:toc}
-
-The further described memory configuration is applicable starting with the release version *1.11*. If you upgrade Flink
-from earlier versions, check the [migration guide](mem_migration.html) because many changes were introduced with the *1.11* release.
-
-Note This memory setup guide is relevant only for the Master!
-The Master memory components have a similar but simpler structure compared to the [TaskManagers' memory configuration](mem_setup_tm.html).
-
-## Configure Total Memory
-
-The simplest way to set up the memory configuration is to configure the [total memory](mem_setup.html#configure-total-memory) for the process.
-If you run the Master process using local [execution mode](#local-execution) you do not need to configure memory options, they will have no effect.
-
-## Detailed configuration
-
-The following table lists all memory components, depicted above, and references Flink configuration options which
-affect the size of the respective components:
-
-| **Component** | **Configuration options** | **Description** |
-| :------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| [Total Process Memory](mem_setup.html#configure-total-memory) | [`jobmanager.memory.process.size`](../config.html#jobmanager-memory-process-size) | The *total process memory* size for the job manager. This includes all the memory that a job manager JVM process consumes, consisting of the *total Flink memory*, *JVM metaspace* and *JVM overhead*. |
-| [Total Flink Memory](mem_setup.html#configure-total-memory) | [`jobmanager.memory.flink.size`](../config.html#jobmanager-memory-flink-size) | The *total Flink memory* size for the job manager. This includes all the memory that a job manager consumes, except for *JVM metaspace* and *JVM overhead*. It consists of [JVM Heap](#configure-jvm-heap) and [Off-heap Memory](#configure-off-heap-memory) Memory. |
-| [JVM Heap](#configure-jvm-heap) | [`jobmanager.memory.heap.size`](../config.html#jobmanager-memory-heap-size) | *JVM Heap* memory size for job manager. |
-| [Off-heap Memory](#configure-off-heap-memory) | [`jobmanager.memory.off-heap.size`](../config.html#jobmanager-memory-off-heap-size) | *Off-heap* memory size for job manager. This option covers all off-heap memory usage including direct and native memory allocation. |
-| [JVM metaspace](mem_setup.html#jvm-parameters) | [`jobmanager.memory.jvm-metaspace.size`](../config.html#jobmanager-memory-jvm-metaspace-size) | Metaspace size of the Flink JVM process |
-| JVM Overhead | [`jobmanager.memory.jvm-overhead.min`](../config.html#jobmanager-memory-jvm-overhead-min) [`jobmanager.memory.jvm-overhead.max`](../config.html#jobmanager-memory-jvm-overhead-max) [`jobmanager.memory.jvm-overhead.fraction`](../config.html#jobmanager-memory-jvm-overhead-fraction) | Native memory reserved for other JVM overhead: e.g. thread stacks, code cache, garbage collection space etc, it is a [capped fractionated component](mem_setup.html#capped-fractionated-components) of the [total process memory](mem_setup.html#configure-total-memory) |
-{:.table-bordered}
-
-
-### Configure JVM Heap
-
-As mentioned before in the [total memory description](mem_setup.html#configure-total-memory), another way to set up the memory
-for the Master is to specify explicitly the *JVM Heap* size ([`jobmanager.memory.heap.size`](../config.html#jobmanager-memory-heap-size)).
-It gives more control over the available *JVM Heap* which is used by:
-
-* Flink framework (e.g. *Job cache*)
-* User code executed during job submission (e.g. for certain batch sources) or in checkpoint completion callbacks
-
-The required size of *JVM Heap* is mostly driven by the number of running jobs, their structure, and requirements for
-the mentioned user code.
-
-The *Job cache* resides in the *JVM Heap*. It can be configured by
-[`jobstore.cache-size`](../config.html#jobstore-cache-size) which must be less than the configured or derived *JVM Heap* size.
-
-Note If you have configured the *JVM Heap* explicitly, it is recommended to set
-neither *total process memory* nor *total Flink memory*. Otherwise, it may easily lead to memory configuration conflicts.
-The Flink scripts and CLI set the *JVM Heap* size via the JVM parameters *-Xms* and *-Xmx* when they start the Master process, see also [JVM parameters](mem_setup.html#jvm-parameters).
-
-### Configure Off-heap Memory
-
-The *Off-heap* memory component accounts for any type of *JVM direct memory* and *native memory* usage. Therefore, it
-is also set via the corresponding JVM argument: *-XX:MaxDirectMemorySize*, see also [JVM parameters](mem_setup.html#jvm-parameters).
-
-The size of this component can be configured by [`jobmanager.memory.off-heap.size`](../config.html#jobmanager-memory-off-heap-size)
-option. This option can be tuned e.g. if the Master process throws ‘OutOfMemoryError: Direct buffer memory’, see
-[the troubleshooting guide](mem_trouble.html#outofmemoryerror-direct-buffer-memory) for more information.
-
-There can be the following possible sources of *Off-heap* memory consumption:
-
-* Flink framework dependencies (e.g. Akka network communication)
-* User code executed during job submission (e.g. for certain batch sources) or in checkpoint completion callbacks
-
-## Local Execution
-
-If you run Flink locally (e.g. from your IDE) without creating a cluster, then the Master memory configuration options are ignored.
diff --git a/docs/ops/memory/mem_setup_tm.md b/docs/ops/memory/mem_setup_tm.md
index add02e64cfa512..492956fbfef57c 100644
--- a/docs/ops/memory/mem_setup_tm.md
+++ b/docs/ops/memory/mem_setup_tm.md
@@ -32,7 +32,7 @@ The further described memory configuration is applicable starting with the relea
from earlier versions, check the [migration guide](mem_migration.html) because many changes were introduced with the *1.10* release.
Note This memory setup guide is relevant only for TaskManagers!
-The TaskManager memory components have a similar but more sophisticated structure compared to the [memory model of the Master process](mem_setup_master.html).
+The TaskManager memory components have a similar but more sophisticated structure compared to the [memory model of the JobManager process]({% link ops/memory/mem_setup_jobmanager.md %}).
## Configure Total Memory
diff --git a/docs/ops/memory/mem_setup_tm.zh.md b/docs/ops/memory/mem_setup_tm.zh.md
index 862a268188014c..a3cafef83f581e 100644
--- a/docs/ops/memory/mem_setup_tm.zh.md
+++ b/docs/ops/memory/mem_setup_tm.zh.md
@@ -22,9 +22,8 @@ specific language governing permissions and limitations
under the License.
-->
-Apache Flink 基于 JVM 的高效处理能力,依赖于其对各组件内存用量的细致掌控。
-考虑到用户在 Flink 上运行的应用的多样性,尽管社区已经努力为所有配置项提供合理的默认值,仍无法满足所有情况下的需求。
-为了给用户生产提供最大化的价值, Flink 允许用户在整体上以及细粒度上对集群的内存分配进行调整。
+Flink 的 TaskManager 负责执行用户代码。
+根据实际需求为 TaskManager 配置内存将有助于减少 Flink 的资源占用,增强作业运行的稳定性。
* toc
{:toc}
@@ -32,7 +31,11 @@ Apache Flink 基于 JVM 的高效处理能力,依赖于其对各组件内存
本文接下来介绍的内存配置方法适用于 *1.10* 及以上版本。
Flink 在 1.10 版本中对内存配置部分进行了较大幅度的改动,从早期版本升级的用户请参考[升级指南](mem_migration.html)。
-提示 本篇内存配置文档仅针对 TaskExecutor!关于 JobManager 的内存配置请参考 [JobManager 相关配置参数](../config.html#jobmanager-heap-size)。
+提示
+本篇内存配置文档仅针对 TaskManager!
+与 [JobManager](mem_setup_jobmanager.html) 相比,TaskManager 具有相似但更加复杂的内存模型。
+
+
## 配置总内存
@@ -44,47 +47,36 @@ Flink JVM 进程的*进程总内存(Total Process Memory)*包含了由 Flink
-如果你是在本地运行 Flink(例如在 IDE 中)而非创建一个集群,那么本文介绍的配置并非所有都是适用的,详情请参考[本地执行](mem_detail.html#本地执行)。
-
-其他情况下,配置 Flink 内存最简单的方法就是配置下列两个参数中的任意一个。
-* Flink 总内存([`taskmanager.memory.flink.size`](../config.html#taskmanager-memory-flink-size))
-* 进程总内存([`taskmanager.memory.process.size`](../config.html#taskmanager-memory-process-size))
-
-Flink 会根据默认值或其他配置参数自动调整剩余内存部分的大小。关于各内存部分的更多细节,请参考[相关文档](mem_detail.html)。
-
-对于独立部署模式(Standalone Deployment),如果你希望指定由 Flink 应用本身使用的内存大小,最好选择配置 *Flink 总内存*。
-*Flink 总内存*会进一步划分为 JVM 堆内存、[托管内存](#托管内存)和*直接内存*。
+如果你是在本地运行 Flink(例如在 IDE 中)而非创建一个集群,那么本文介绍的配置并非所有都是适用的,详情请参考[本地执行](#local-execution)。
-通过配置*进程总内存*可以指定由 Flink *JVM 进程*使用的总内存大小。
-对于容器化部署模式(Containerized Deployment),这相当于申请的容器(Container)大小,详情请参考[如何配置容器内存](mem_tuning.html#容器container的内存配置)([Kubernetes](../deployment/kubernetes.html)、[Yarn](../deployment/yarn_setup.html) 或 [Mesos](../deployment/mesos.html))。
+其他情况下,配置 Flink 内存最简单的方法就是[配置总内存](mem_setup.html#configure-total-memory)。
+此外,Flink 也支持[更细粒度的内存配置方式](#configure-heap-and-managed-memory)。
-此外,还可以通过设置[任务堆内存(Task Heap Memory)](#任务算子堆内存)和[托管内存](#托管内存)的方式进行内存配置([`taskmanager.memory.task.heap.size`](../config.html#taskmanager-memory-task-heap-size) 和 [`taskmanager.memory.managed.size`](../config.html#taskmanager-memory-managed-size))。
-这是一种更细粒度的配置方式,更多细节请参考[相关文档](#配置堆内存和托管内存)。
+Flink 会根据默认值或其他配置参数自动调整剩余内存部分的大小。
+接下来的章节将介绍关于各内存部分的更多细节。
-提示 以上三种方式中,用户需要至少选择其中一种进行配置(本地运行除外),否则 Flink 将无法启动。
-这意味着,用户需要从以下无默认值的配置参数(或参数组合)中选择一个给出明确的配置:
-* [`taskmanager.memory.flink.size`](../config.html#taskmanager-memory-flink-size)
-* [`taskmanager.memory.process.size`](../config.html#taskmanager-memory-process-size)
-* [`taskmanager.memory.task.heap.size`](../config.html#taskmanager-memory-task-heap-size) 和 [`taskmanager.memory.managed.size`](../config.html#taskmanager-memory-managed-size)
-
-提示 不建议同时设置*进程总内存*和 *Flink 总内存*。
-这可能会造成内存配置冲突,从而导致部署失败。
-额外配置其他内存部分时,同样需要注意可能产生的配置冲突。
+
## 配置堆内存和托管内存
-如[配置总内存](#配置总内存)中所述,另一种配置 Flink 内存的方式是同时设置[任务堆内存](#任务算子堆内存)和[托管内存](#托管内存)。
-通过这种方式,用户可以更好地掌控用于 Flink 任务的 JVM 堆内存及 Flink 的[托管内存](#托管内存)大小。
+如[配置总内存](#configure-total-memory)中所述,另一种配置 Flink 内存的方式是同时设置[任务堆内存](#task-operator-heap-memory)和[托管内存](#managed-memory)。
+通过这种方式,用户可以更好地掌控用于 Flink 任务的 JVM 堆内存及 Flink 的[托管内存](#managed-memory)大小。
+
+Flink 会根据默认值或其他配置参数自动调整剩余内存部分的大小。
+关于各内存部分的更多细节,请参考[相关文档](#detailed-memory-model)。
-Flink 会根据默认值或其他配置参数自动调整剩余内存部分的大小。关于各内存部分的更多细节,请参考[相关文档](mem_detail.html)。
+提示
+如果已经明确设置了任务堆内存和托管内存,建议不要再设置*进程总内存*或 *Flink 总内存*,否则可能会造成内存配置冲突。
-提示 如果已经明确设置了任务堆内存和托管内存,建议不要再设置*进程总内存*或 *Flink 总内存*,否则可能会造成内存配置冲突。
+
### 任务(算子)堆内存
如果希望确保指定大小的 JVM 堆内存给用户代码使用,可以明确指定*任务堆内存*([`taskmanager.memory.task.heap.size`](../config.html#taskmanager-memory-task-heap-size))。
指定的内存将被包含在总的 JVM 堆空间中,专门用于 Flink 算子及用户代码的执行。
+
+
### 托管内存
*托管内存*是由 Flink 负责分配和管理的本地(堆外)内存。
@@ -99,21 +91,90 @@ Flink 会根据默认值或其他配置参数自动调整剩余内存部分的
当同时指定二者时,会优先采用指定的大小(Size)。
若二者均未指定,会根据[默认占比](../config.html#taskmanager-memory-managed-fraction)进行计算。
-请同时参考[如何配置 State Backend 内存](mem_tuning.html#state-backend-的内存配置)以及[如何配置批处理作业内存](mem_tuning.html#批处理作业的内存配置)。
+请同时参考[如何配置 State Backend 内存](mem_tuning.html#configure-memory-for-state-backends)以及[如何配置批处理作业内存](mem_tuning.html#configure-memory-for-batch-jobs)。
+
+
## 配置堆外内存(直接内存或本地内存)
-用户代码中分配的堆外内存被归为*任务堆外内存(Task Off-heap Memory),可以通过 [`taskmanager.memory.task.off-heap.size`](../config.html#taskmanager-memory-task-off-heap-size) 指定。
+用户代码中分配的堆外内存被归为*任务堆外内存(Task Off-heap Memory)*,可以通过 [`taskmanager.memory.task.off-heap.size`](../config.html#taskmanager-memory-task-off-heap-size) 指定。
-提示 你也可以调整[框架推外内存(Framework Off-heap Memory)](mem_detail.html#框架内存)。
+提示
+你也可以调整[框架堆外内存(Framework Off-heap Memory)](#framework-memory)。
这是一个进阶配置,建议仅在确定 Flink 框架需要更多的内存时调整该配置。
-Flink 将*框架堆外内存*和*任务堆外内存*都计算在 JVM 的*直接内存*限制中,请参考 [JVM 参数](mem_detail.html#jvm-参数)。
+Flink 将*框架堆外内存*和*任务堆外内存*都计算在 JVM 的*直接内存*限制中,请参考 [JVM 参数](mem_setup.html#jvm-parameters)。
-提示 本地内存(非直接内存)也可以被归在*框架堆外内存*或*任务推外内存*中,在这种情况下 JVM 的*直接内存*限制可能会高于实际需求。
+提示
+本地内存(非直接内存)也可以被归在*框架堆外内存*或*任务堆外内存*中,在这种情况下 JVM 的*直接内存*限制可能会高于实际需求。
-提示 *网络内存(Network Memory)*同样被计算在 JVM *直接内存*中。
+提示
+*网络内存(Network Memory)*同样被计算在 JVM *直接内存*中。
Flink 会负责管理网络内存,保证其实际用量不会超过配置大小。
因此,调整*网络内存*的大小不会对其他堆外内存有实质上的影响。
-请参考[内存模型详解](mem_detail.html)。
+请参考[内存模型详解](#detailed-memory-model)。
+
+
+
+## 内存模型详解
+
+
+
+
+
+
+
+如上图所示,下表中列出了 Flink TaskManager 内存模型的所有组成部分,以及影响其大小的相关配置参数。
+
+| **组成部分** | **配置参数** | **描述** |
+| :------------------------------------------------------------------| :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| [框架堆内存(Framework Heap Memory)](#framework-memory) | [`taskmanager.memory.framework.heap.size`](../config.html#taskmanager-memory-framework-heap-size) | 用于 Flink 框架的 JVM 堆内存(进阶配置)。 |
+| [任务堆内存(Task Heap Memory)](#task-operator-heap-memory) | [`taskmanager.memory.task.heap.size`](../config.html#taskmanager-memory-task-heap-size) | 用于 Flink 应用的算子及用户代码的 JVM 堆内存。 |
+| [托管内存(Managed memory)](#managed-memory) | [`taskmanager.memory.managed.size`](../config.html#taskmanager-memory-managed-size) [`taskmanager.memory.managed.fraction`](../config.html#taskmanager-memory-managed-fraction) | 由 Flink 管理的用于排序、哈希表、缓存中间结果及 RocksDB State Backend 的本地内存。 |
+| [框架堆外内存(Framework Off-heap Memory)](#framework-memory) | [`taskmanager.memory.framework.off-heap.size`](../config.html#taskmanager-memory-framework-off-heap-size) | 用于 Flink 框架的[堆外内存(直接内存或本地内存)](#configure-off-heap-memory-direct-or-native)(进阶配置)。 |
+| [任务堆外内存(Task Off-heap Memory)](#configure-off-heap-memory-direct-or-native)| [`taskmanager.memory.task.off-heap.size`](../config.html#taskmanager-memory-task-off-heap-size) | 用于 Flink 应用的算计及用户代码的[堆外内存(直接内存或本地内存)](#configure-off-heap-memory-direct-or-native)。 |
+| 网络内存(Network Memory) | [`taskmanager.memory.network.min`](../config.html#taskmanager-memory-network-min) [`taskmanager.memory.network.max`](../config.html#taskmanager-memory-network-max) [`taskmanager.memory.network.fraction`](../config.html#taskmanager-memory-network-fraction) | 用于任务之间数据传输的直接内存(例如网络传输缓冲)。该内存部分为基于 [Flink 总内存](mem_setup.html#configure-total-memory)的[受限的等比内存部分](mem_setup.html#capped-fractionated-components)。 |
+| [JVM Metaspace](mem_setup.html#jvm-parameters) | [`taskmanager.memory.jvm-metaspace.size`](../config.html#taskmanager-memory-jvm-metaspace-size) | Flink JVM 进程的 Metaspace。 |
+| JVM 开销 | [`taskmanager.memory.jvm-overhead.min`](../config.html#taskmanager-memory-jvm-overhead-min) [`taskmanager.memory.jvm-overhead.max`](../config.html#taskmanager-memory-jvm-overhead-max) [`taskmanager.memory.jvm-overhead.fraction`](../config.html#taskmanager-memory-jvm-overhead-fraction) | 用于其他 JVM 开销的本地内存,例如栈空间、垃圾回收空间等。该内存部分为基于[进程总内存](mem_setup.html#configure-total-memory)的[受限的等比内存部分](mem_setup.html#capped-fractionated-components)。 |
+{:.table-bordered}
+
+
+我们可以看到,有些内存部分的大小可以直接通过一个配置参数进行设置,有些则需要根据多个参数进行调整。
+
+
+
+## 框架内存
+
+通常情况下,不建议对*框架堆内存*和*框架堆外内存*进行调整。
+除非你非常肯定 Flink 的内部数据结构及操作需要更多的内存。
+这可能与具体的部署环境及作业结构有关,例如非常高的并发度。
+此外,Flink 的部分依赖(例如 Hadoop)在某些特定的情况下也可能会需要更多的直接内存或本地内存。
+
+提示
+不管是堆内存还是堆外内存,Flink 中的框架内存和任务内存之间目前是没有隔离的。
+对框架和任务内存的区分,主要是为了在后续版本中做进一步优化。
+
+
+
+## 本地执行
+如果你是将 Flink 作为一个单独的 Java 程序运行在你的电脑本地而非创建一个集群(例如在 IDE 中),那么只有下列配置会生效,其他配置参数则不会起到任何效果:
+
+| **组成部分** | **配置参数** | **本地执行时的默认值** |
+| :------------------------------------------- | :---------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------ |
+| 任务堆内存 | [`taskmanager.memory.task.heap.size`](../config.html#taskmanager-memory-task-heap-size) | 无穷大 |
+| 任务堆外内存 | [`taskmanager.memory.task.off-heap.size`](../config.html#taskmanager-memory-task-off-heap-size) | 无穷大 |
+| 托管内存 | [`taskmanager.memory.managed.size`](../config.html#taskmanager-memory-managed-size) | 128Mb |
+| 网络内存 | [`taskmanager.memory.network.min`](../config.html#taskmanager-memory-network-min) [`taskmanager.memory.network.max`](../config.html#taskmanager-memory-network-max) | 64Mb |
+{:.table-bordered}
+
+
+本地执行模式下,上面列出的所有内存部分均可以但不是必须进行配置。
+如果未配置,则会采用默认值。
+其中,[任务堆内存](#task-operator-heap-memory)和*任务堆外内存*的默认值无穷大(*Long.MAX_VALUE* 字节),以及[托管内存](#managed-memory)的默认值 128Mb 均只针对本地执行模式。
+
+提示
+这种情况下,任务堆内存的大小与实际的堆空间大小无关。
+该配置参数可能与后续版本中的进一步优化相关。
+本地执行模式下,JVM 堆空间的实际大小不受 Flink 掌控,而是取决于本地执行进程是如何启动的。
+如果希望控制 JVM 的堆空间大小,可以在启动进程时明确地指定相关的 JVM 参数,即 *-Xmx* 和 *-Xms*。
diff --git a/docs/ops/memory/mem_trouble.md b/docs/ops/memory/mem_trouble.md
index d0b5312e89eef1..1f68ccbcf5a901 100644
--- a/docs/ops/memory/mem_trouble.md
+++ b/docs/ops/memory/mem_trouble.md
@@ -37,7 +37,7 @@ greater than 1, etc.) or configuration conflicts. Check the documentation chapte
The exception usually indicates that the *JVM Heap* is too small. You can try to increase the JVM Heap size
by increasing [total memory](mem_setup.html#configure-total-memory). You can also directly increase
[task heap memory](mem_setup_tm.html#task-operator-heap-memory) for TaskManagers or
-[JVM Heap memory](mem_setup_master.html#configure-jvm-heap) for Masters.
+[JVM Heap memory]({% link ops/memory/mem_setup_jobmanager.md %}#configure-jvm-heap) for JobManagers.
Note You can also increase the [framework heap memory](mem_setup_tm.html#framework-memory)
for TaskManagers, but you should only change this option if you are sure the Flink framework itself needs more memory.
@@ -48,13 +48,13 @@ The exception usually indicates that the JVM *direct memory* limit is too small
Check whether user code or other external dependencies use the JVM *direct memory* and that it is properly accounted for.
You can try to increase its limit by adjusting direct off-heap memory.
See also how to configure off-heap memory for [TaskManagers](mem_setup_tm.html#configure-off-heap-memory-direct-or-native),
-[Masters](mem_setup_master.html#configure-off-heap-memory) and the [JVM arguments](mem_setup.html#jvm-parameters) which Flink sets.
+[JobManagers]({% link ops/memory/mem_setup_jobmanager.md %}#configure-off-heap-memory) and the [JVM arguments](mem_setup.html#jvm-parameters) which Flink sets.
## OutOfMemoryError: Metaspace
The exception usually indicates that [JVM metaspace limit](mem_setup.html#jvm-parameters) is configured too small.
You can try to increase the JVM metaspace option for [TaskManagers](../config.html#taskmanager-memory-jvm-metaspace-size)
-or [Masters](../config.html#jobmanager-memory-jvm-metaspace-size).
+or [JobManagers](../config.html#jobmanager-memory-jvm-metaspace-size).
## IOException: Insufficient number of network buffers
@@ -72,8 +72,13 @@ If a Flink container tries to allocate memory beyond its requested size (Yarn, M
this usually indicates that Flink has not reserved enough native memory. You can observe this either by using an external
monitoring system or from the error messages when a container gets killed by the deployment environment.
+If you encounter this problem in the *JobManager* process, you can also enable the *JVM Direct Memory* limit by setting the
+[`jobmanager.memory.enable-jvm-direct-memory-limit`](../config.html#jobmanager-memory-enable-jvm-direct-memory-limit) option
+to exclude possible *JVM Direct Memory* leak.
+
If [RocksDBStateBackend](../state/state_backends.html#the-rocksdbstatebackend) is used, and the memory controlling is disabled,
you can try to increase the TaskManager's [managed memory](mem_setup.html#managed-memory).
Alternatively, you can increase the [JVM Overhead](mem_setup.html#capped-fractionated-components).
+
See also [how to configure memory for containers](mem_tuning.html#configure-memory-for-containers).
diff --git a/docs/ops/memory/mem_trouble.zh.md b/docs/ops/memory/mem_trouble.zh.md
index 0ecb5ada5deb16..7daa8de1e3e88a 100644
--- a/docs/ops/memory/mem_trouble.zh.md
+++ b/docs/ops/memory/mem_trouble.zh.md
@@ -27,43 +27,51 @@ under the License.
## IllegalConfigurationException
-如果遇到从 *TaskExecutorProcessUtils* 抛出的 *IllegalConfigurationException* 异常,这通常说明您的配置参数中存在无效值(例如内存大小为负数、占比大于 1 等)或者配置冲突。
-请根据异常信息,确认[内存模型详解](mem_detail.html)中与出错的内存部分对应章节的内容。
+如果遇到从 *TaskExecutorProcessUtils* 或 *JobManagerProcessUtils* 抛出的 *IllegalConfigurationException* 异常,这通常说明您的配置参数中存在无效值(例如内存大小为负数、占比大于 1 等)或者配置冲突。
+请根据异常信息,确认出错的内存部分的相关文档及[配置信息](../config.html#memory-configuration)。
## OutOfMemoryError: Java heap space
该异常说明 JVM 的堆空间过小。
-可以通过增大[总内存](mem_setup.html#配置总内存)或[任务堆内存](mem_setup.html#任务算子堆内存)的方法来增大 JVM 堆空间。
+可以通过增大[总内存](mem_setup.html#configure-total-memory)、TaskManager 的[任务堆内存](mem_setup_tm.html#task-operator-heap-memory)、JobManager 的 [JVM 堆内存](mem_setup_jobmanager.html#configure-jvm-heap)等方法来增大 JVM 堆空间。
-提示 也可以增大[框架堆内存](mem_detail.html#框架内存)。这是一个进阶配置,只有在确认是 Flink 框架自身需要更多内存时才应该去调整。
+提示
+也可以增大 TaskManager 的[框架堆内存](mem_setup_tm.html#framework-memory)。
+这是一个进阶配置,只有在确认是 Flink 框架自身需要更多内存时才应该去调整。
## OutOfMemoryError: Direct buffer memory
该异常通常说明 JVM 的*直接内存*限制过小,或者存在*直接内存泄漏(Direct Memory Leak)*。
请确认用户代码及外部依赖中是否使用了 JVM *直接内存*,以及如果使用了直接内存,是否配置了足够的内存空间。
-可以通过调整[堆外内存](mem_detail.html)来增大直接内存限制。
-请同时参考[如何配置堆外内存](mem_setup.html#配置堆外内存直接内存或本地内存))以及 Flink 设置的 [JVM 参数](mem_detail.html#jvm-参数)。
+可以通过调整堆外内存来增大直接内存限制。
+有关堆外内存的配置方法,请参考 [TaskManager](mem_setup_tm.html#configure-off-heap-memory-direct-or-native)、[JobManager](mem_setup_jobmanager.html#configure-off-heap-memory) 以及 [JVM 参数](mem_setup.html#jvm-parameters)的相关文档。
## OutOfMemoryError: Metaspace
-该异常说明 [JVM Metaspace 限制](mem_detail.html#jvm-参数)过小。
-可以尝试调整 [JVM Metaspace 参数](../config.html#taskmanager-memory-jvm-metaspace-size)。
+该异常说明 [JVM Metaspace 限制](mem_setup.html#jvm-parameters)过小。
+可以尝试调整 [TaskManager](../config.html#taskmanager-memory-jvm-metaspace-size)、[JobManager](../config.html#jobmanager-memory-jvm-metaspace-size) 的 JVM Metaspace。
## IOException: Insufficient number of network buffers
-该异常通常说明[网络内存](mem_detail.html)过小。
+该异常仅与 TaskManager 相关。
+
+该异常通常说明[网络内存](mem_setup_tm.html#detailed-memory-model)过小。
可以通过调整以下配置参数增大*网络内存*:
* [`taskmanager.memory.network.min`](../config.html#taskmanager-memory-network-min)
* [`taskmanager.memory.network.max`](../config.html#taskmanager-memory-network-max)
* [`taskmanager.memory.network.fraction`](../config.html#taskmanager-memory-network-fraction)
+
+
## 容器(Container)内存超用
-如果 TaskExecutor 容器尝试分配超过其申请大小的内存(Yarn、Mesos 或 Kubernetes),这通常说明 Flink 没有预留出足够的本地内存。
+如果 Flink 容器尝试分配超过其申请大小的内存(Yarn、Mesos 或 Kubernetes),这通常说明 Flink 没有预留出足够的本地内存。
可以通过外部监控系统或者容器被部署环境杀掉时的错误信息判断是否存在容器内存超用。
-如果使用了 [RocksDBStateBackend](../state/state_backends.html#rocksdbstatebackend) 且没有开启内存控制,也可以尝试增大[托管内存](mem_setup.html#托管内存)。
+对于 *JobManager* 进程,你还可以尝试启用 *JVM 直接内存限制*([`jobmanager.memory.enable-jvm-direct-memory-limit`](../config.html#jobmanager-memory-enable-jvm-direct-memory-limit)),以排除 *JVM 直接内存泄漏*的可能性。
+
+如果使用了 [RocksDBStateBackend](../state/state_backends.html#rocksdbstatebackend) 且没有开启内存控制,也可以尝试增大 TaskManager 的[托管内存](mem_setup.html#managed-memory)。
-此外,还可以尝试增大 [JVM 开销](mem_detail.html)。
+此外,还可以尝试增大 [JVM 开销](mem_setup.html#capped-fractionated-components)。
-请参考[如何配置容器内存](mem_tuning.html#容器container的内存配置)。
+请参考[如何配置容器内存](mem_tuning.html#configure-memory-for-containers)。
diff --git a/docs/ops/memory/mem_tuning.zh.md b/docs/ops/memory/mem_tuning.zh.md
index 9fb950d8ff1380..e4f8c9954b3021 100644
--- a/docs/ops/memory/mem_tuning.zh.md
+++ b/docs/ops/memory/mem_tuning.zh.md
@@ -22,55 +22,66 @@ specific language governing permissions and limitations
under the License.
-->
-本文在的基本的[配置指南](mem_setup.html)的基础上,介绍如何根据具体的使用场景调整 TaskExecutor 的内存配置,以及在不同使用场景下分别需要重点关注哪些配置参数。
+本文在的基本的[配置指南](mem_setup.html)的基础上,介绍如何根据具体的使用场景调整内存配置,以及在不同使用场景下分别需要重点关注哪些配置参数。
* toc
{:toc}
+
+
## 独立部署模式(Standalone Deployment)下的内存配置
-[独立部署模式](../deployment/cluster_setup.html),我们通常更关注 Flink 应用本身使用的内存大小。
-建议配置 [Flink 总内存](mem_setup.html#配置总内存)([`taskmanager.memory.flink.size`](../config.html#taskmanager-memory-flink-size))或者它的[组成部分](mem_detail.html)。
+[独立部署模式](../deployment/cluster_setup.html)下,我们通常更关注 Flink 应用本身使用的内存大小。
+建议配置 [Flink 总内存](mem_setup.html#configure-total-memory)([`taskmanager.memory.flink.size`](../config.html#taskmanager-memory-flink-size) 或者 [`jobmanager.memory.flink.size`](../config.html#jobmanager-memory-flink-size.html))或其组成部分。
此外,如果出现 [Metaspace 不足的问题](mem_trouble.html#outofmemoryerror-metaspace),可以调整 *JVM Metaspace* 的大小。
这种情况下通常无需配置*进程总内存*,因为不管是 Flink 还是部署环境都不会对 *JVM 开销* 进行限制,它只与机器的物理资源相关。
+
+
## 容器(Container)的内存配置
-在容器化部署模式(Containerized Deployment)下([Kubernetes](../deployment/kubernetes.html)、[Yarn](../deployment/yarn_setup.html) 或 [Mesos](../deployment/mesos.html)),建议配置[进程总内存](mem_setup.html#配置总内存)([`taskmanager.memory.process.size`](../config.html#taskmanager-memory-process-size))。
+在容器化部署模式(Containerized Deployment)下([Kubernetes](../deployment/kubernetes.html)、[Yarn](../deployment/yarn_setup.html) 或 [Mesos](../deployment/mesos.html)),建议配置[进程总内存](mem_setup.html#configure-total-memory)([`taskmanager.memory.process.size`](../config.html#taskmanager-memory-process-size) 或者 [`jobmanager.memory.process.size`](../config.html#jobmanager-memory-process-size))。
该配置参数用于指定分配给 Flink *JVM 进程*的总内存,也就是需要申请的容器大小。
-提示 如果配置了 *Flink 总内存*,Flink 会自动加上 JVM 相关的内存部分,根据推算出的*进程总内存*大小申请容器。
-请参考[内存模型详解](mem_detail.html)。
+提示
+如果配置了 *Flink 总内存*,Flink 会自动加上 JVM 相关的内存部分,根据推算出的*进程总内存*大小申请容器。
-请参考[容器内存超用](mem_trouble.html#容器container内存超用)中的相关描述。
+
+请参考[容器内存超用](mem_trouble.html#container-memory-exceeded)中的相关描述。
+
+
## State Backend 的内存配置
+本章节内容仅与 TaskManager 相关。
+
在部署 Flink 流处理应用时,可以根据 [State Backend](../state/state_backends.html) 的类型对集群的配置进行优化。
### Heap State Backend
执行无状态作业或者使用 Heap State Backend([MemoryStateBackend](../state/state_backends.html#memorystatebackend)
-或 [FsStateBackend](../state/state_backends.html#fsstatebackend))时,建议将[托管内存](mem_setup.html#托管内存)设置为 0。
+或 [FsStateBackend](../state/state_backends.html#fsstatebackend))时,建议将[托管内存](mem_setup_tm.html#managed-memory)设置为 0。
这样能够最大化分配给 JVM 上用户代码的内存。
### RocksDB State Backend
[RocksDBStateBackend](../state/state_backends.html#rocksdbstatebackend) 使用本地内存。
-默认情况下,RocksDB 会限制其内存用量不超过用户配置的[*托管内存*](mem_setup.html#托管内存)。
+默认情况下,RocksDB 会限制其内存用量不超过用户配置的[*托管内存*](mem_setup_tm.html#managed-memory)。
因此,使用这种方式存储状态时,配置足够多的*托管内存*是十分重要的。
-如果你关闭了 RocksDB 的内存控制,那么在容器化部署模式下如果 RocksDB 分配的内存超出了申请容器的大小([进程总内存](mem_setup.html#配置总内存)),可能会造成 TaskExecutor 被部署环境杀掉。
+如果你关闭了 RocksDB 的内存控制,那么在容器化部署模式下如果 RocksDB 分配的内存超出了申请容器的大小([进程总内存](mem_setup.html#configure-total-memory)),可能会造成 TaskExecutor 被部署环境杀掉。
请同时参考[如何调整 RocksDB 内存](../state/large_state_tuning.html#tuning-rocksdb-memory)以及 [state.backend.rocksdb.memory.managed](../config.html#state-backend-rocksdb-memory-managed)。
+
+
## 批处理作业的内存配置
-Flink 批处理算子使用[托管内存](../memory/mem_setup.html#托管内存)来提高处理效率。
+Flink 批处理算子使用[托管内存](../memory/mem_setup_tm.html#managed-memory)来提高处理效率。
算子运行时,部分操作可以直接在原始数据上进行,而无需将数据反序列化成 Java 对象。
-这意味着[托管内存](../memory/mem_setup.html#托管内存)对应用的性能具有实质上的影响。
-因此 Flink 会在不超过其配置限额的前提下,尽可能分配更多的[托管内存](../memory/mem_setup.html#托管内存)。
+这意味着[托管内存](../memory/mem_setup_tm.html#managed-memory)对应用的性能具有实质上的影响。
+因此 Flink 会在不超过其配置限额的前提下,尽可能分配更多的[托管内存](../memory/mem_setup_tm.html#managed-memory)。
Flink 明确知道可以使用的内存大小,因此可以有效避免 `OutOfMemoryError` 的发生。
-当[托管内存](../memory/mem_setup.html#托管内存)不足时,Flink 会优雅的将数据落盘。
+当[托管内存](../memory/mem_setup_tm.html#managed-memory)不足时,Flink 会优雅地将数据落盘。
diff --git a/docs/ops/plugins.md b/docs/ops/plugins.md
index ea203ff418ea1e..38b36342002620 100644
--- a/docs/ops/plugins.md
+++ b/docs/ops/plugins.md
@@ -26,7 +26,7 @@ under the License.
Plugins facilitate a strict separation of code through restricted classloaders. Plugins cannot
access classes from other plugins or from Flink that have not been specifically whitelisted. This
strict isolation allows plugins to contain conflicting versions of the same library without the need
-to relocate classes or to converge to common versions. Currently, only file systems are pluggable
+to relocate classes or to converge to common versions. Currently, file systems and metric reporters are pluggable
but in the future, connectors, formats, and even user code should also be pluggable.
* This will be replaced by the TOC
@@ -114,4 +114,9 @@ The whitelisted classes mainly consists of the necessary interfaces to implement
Furthermore, loggers are whitelisted, so that they are configured properly.
-->
+## Metric Reporters
+
+All [metric reporters](../monitoring/metrics.html#Reporter) that Flink provides can be used as plugins.
+See the [metrics](../monitoring/metrics.html) documentation for more details.
+
{% top %}
diff --git a/docs/ops/plugins.zh.md b/docs/ops/plugins.zh.md
index ea203ff418ea1e..38b36342002620 100644
--- a/docs/ops/plugins.zh.md
+++ b/docs/ops/plugins.zh.md
@@ -26,7 +26,7 @@ under the License.
Plugins facilitate a strict separation of code through restricted classloaders. Plugins cannot
access classes from other plugins or from Flink that have not been specifically whitelisted. This
strict isolation allows plugins to contain conflicting versions of the same library without the need
-to relocate classes or to converge to common versions. Currently, only file systems are pluggable
+to relocate classes or to converge to common versions. Currently, file systems and metric reporters are pluggable
but in the future, connectors, formats, and even user code should also be pluggable.
* This will be replaced by the TOC
@@ -114,4 +114,9 @@ The whitelisted classes mainly consists of the necessary interfaces to implement
Furthermore, loggers are whitelisted, so that they are configured properly.
-->
+## Metric Reporters
+
+All [metric reporters](../monitoring/metrics.html#Reporter) that Flink provides can be used as plugins.
+See the [metrics](../monitoring/metrics.html) documentation for more details.
+
{% top %}
diff --git a/docs/ops/python_shell.zh.md b/docs/ops/python_shell.zh.md
index e5f2a6c6093c25..2f561c7d70a5e8 100644
--- a/docs/ops/python_shell.zh.md
+++ b/docs/ops/python_shell.zh.md
@@ -27,7 +27,7 @@ Flink附带了一个集成的交互式Python Shell。
本地安装Flink,请看[本地安装](deployment/local.html)页面。
您也可以从源码安装Flink,请看[从源码构建 Flink](../flinkDev/building.html)页面。
-注意 Python Shell会调用“python”命令。关于Python执行环境的要求,请参考Python Table API[环境安装]({{ site.baseurl }}/dev/dev/table/python/installation.html)。
+注意 Python Shell会调用“python”命令。关于Python执行环境的要求,请参考Python Table API[环境安装]({{ site.baseurl }}/dev/table/python/installation.html)。
你可以通过PyPi安装PyFlink,然后使用Python Shell:
diff --git a/docs/ops/security-ssl.md b/docs/ops/security-ssl.md
index eade9dfa66455c..f7cd45acaf95ac 100644
--- a/docs/ops/security-ssl.md
+++ b/docs/ops/security-ssl.md
@@ -216,7 +216,7 @@ However, as mentioned above, the REST endpoint does not authenticate clients and
**REST Endpoint (simple self signed certificate)**
This example shows how to create a simple keystore / truststore pair. The truststore does not contain the primary key and can
-be shared with other applications. In this example, *myhost.company.org / ip:10.0.2.15* is the node (or service) for the Flink master.
+be shared with other applications. In this example, *myhost.company.org / ip:10.0.2.15* is the node (or service) for the JobManager.
{% highlight bash %}
keytool -genkeypair -alias flink.rest -keystore rest.keystore -dname "CN=myhost.company.org" -ext "SAN=dns:myhost.company.org,ip:10.0.2.15" -storepass rest_keystore_password -keyalg RSA -keysize 4096 -storetype PKCS12
@@ -248,7 +248,7 @@ keytool -importcert -keystore ca.truststore -alias ca -storepass ca_truststore_p
{% endhighlight %}
Now create a keystore for the REST endpoint with a certificate signed by the above CA.
-Let *flink.company.org / ip:10.0.2.15* be the hostname of the Flink master (JobManager).
+Let *flink.company.org / ip:10.0.2.15* be the hostname of the JobManager.
{% highlight bash %}
keytool -genkeypair -alias flink.rest -keystore rest.signed.keystore -dname "CN=flink.company.org" -ext "SAN=dns:flink.company.org" -storepass rest_keystore_password -keyalg RSA -keysize 4096 -storetype PKCS12
@@ -300,7 +300,7 @@ For YARN and Mesos, you can use the tools of Yarn and Mesos to help:
- Configuring security for internal communication is exactly the same as in the example above.
- To secure the REST endpoint, you need to issue the REST endpoint's certificate such that it is valid for all hosts
- that the Flink master may get deployed to. This can be done with a wild card DNS name, or by adding multiple DNS names.
+ that the JobManager may get deployed to. This can be done with a wild card DNS name, or by adding multiple DNS names.
- The easiest way to deploy keystores and truststore is by YARN client's *ship files* option (`-yt`).
Copy the keystore and truststore files into a local directory (say `deploy-keys/`) and start the YARN session as
diff --git a/docs/ops/security-ssl.zh.md b/docs/ops/security-ssl.zh.md
index ae1a36a8ba269e..c7ce3e687453b3 100644
--- a/docs/ops/security-ssl.zh.md
+++ b/docs/ops/security-ssl.zh.md
@@ -63,7 +63,7 @@ added to the container images, or attached to the YARN deployment.
the recommendation is to still have a dedicated key pair / certificate for the Flink deployment, signed by that CA.
However, the TrustStore must then also contain the CA's public certificate tho accept the deployment's certificate
during the SSL handshake (requirement in JDK TrustStore implementation).
-
+
**NOTE:** Because of that, it is critical that you specify the fingerprint of the deployment certificate
(`security.ssl.internal.cert.fingerprint`), when it is not self-signed, to pin that certificate as the only trusted
certificate and prevent the TrustStore from trusting all certificates signed by that CA.
@@ -129,7 +129,7 @@ security.ssl.internal.truststore: /path/to/file.truststore
security.ssl.internal.truststore-password: truststore_password
{% endhighlight %}
-When using a certificate that is not self-signed, but signed by a CA, you need to use certificate pinning to allow only a
+When using a certificate that is not self-signed, but signed by a CA, you need to use certificate pinning to allow only a
a specific certificate to be trusted when establishing the connectivity.
{% highlight yaml %}
@@ -216,7 +216,7 @@ However, as mentioned above, the REST endpoint does not authenticate clients and
**REST Endpoint (simple self signed certificate)**
This example shows how to create a simple keystore / truststore pair. The truststore does not contain the primary key and can
-be shared with other applications. In this example, *myhost.company.org / ip:10.0.2.15* is the node (or service) for the Flink master.
+be shared with other applications. In this example, *myhost.company.org / ip:10.0.2.15* is the node (or service) for the JobManager.
{% highlight bash %}
keytool -genkeypair -alias flink.rest -keystore rest.keystore -dname "CN=myhost.company.org" -ext "SAN=dns:myhost.company.org,ip:10.0.2.15" -storepass rest_keystore_password -keyalg RSA -keysize 4096 -storetype PKCS12
@@ -248,7 +248,7 @@ keytool -importcert -keystore ca.truststore -alias ca -storepass ca_truststore_p
{% endhighlight %}
Now create a keystore for the REST endpoint with a certificate signed by the above CA.
-Let *flink.company.org / ip:10.0.2.15* be the hostname of the Flink master (JobManager).
+Let *flink.company.org / ip:10.0.2.15* be the hostname of the JobManager.
{% highlight bash %}
keytool -genkeypair -alias flink.rest -keystore rest.signed.keystore -dname "CN=flink.company.org" -ext "SAN=dns:flink.company.org" -storepass rest_keystore_password -keyalg RSA -keysize 4096 -storetype PKCS12
@@ -300,7 +300,7 @@ For YARN and Mesos, you can use the tools of Yarn and Mesos to help:
- Configuring security for internal communication is exactly the same as in the example above.
- To secure the REST endpoint, you need to issue the REST endpoint's certificate such that it is valid for all hosts
- that the Flink master may get deployed to. This can be done with a wild card DNS name, or by adding multiple DNS names.
+ that the JobManager may get deployed to. This can be done with a wild card DNS name, or by adding multiple DNS names.
- The easiest way to deploy keystores and truststore is by YARN client's *ship files* option (`-yt`).
Copy the keystore and truststore files into a local directory (say `deploy-keys/`) and start the YARN session as
diff --git a/docs/ops/state/checkpoints.md b/docs/ops/state/checkpoints.md
index cf54b070169030..c9d4f08340ae6f 100644
--- a/docs/ops/state/checkpoints.md
+++ b/docs/ops/state/checkpoints.md
@@ -32,7 +32,7 @@ Checkpoints make state in Flink fault tolerant by allowing state and the
corresponding stream positions to be recovered, thereby giving the application
the same semantics as a failure-free execution.
-See [Checkpointing]({{ site.baseurl }}/dev/stream/state/checkpointing.html) for how to enable and
+See [Checkpointing]({% link dev/stream/state/checkpointing.md %}) for how to enable and
configure checkpoints for your program.
## Retained Checkpoints
@@ -113,4 +113,51 @@ above).
$ bin/flink run -s :checkpointMetaDataPath [:runArgs]
{% endhighlight %}
+### Unaligned checkpoints
+
+Starting with Flink 1.11, checkpoints can be unaligned.
+[Unaligned checkpoints]({% link concepts/stateful-stream-processing.md
+%}#unaligned-checkpointing) contain in-flight data (i.e., data stored in
+buffers) as part of the checkpoint state, which allows checkpoint barriers to
+overtake these buffers. Thus, the checkpoint duration becomes independent of the
+current throughput as checkpoint barriers are effectively not embedded into
+the stream of data anymore.
+
+You should use unaligned checkpoints if your checkpointing durations are very
+high due to backpressure. Then, checkpointing time becomes mostly
+independent of the end-to-end latency. Be aware unaligned checkpointing
+adds to I/O to the state backends, so you shouldn't use it when the I/O to
+the state backend is actually the bottleneck during checkpointing.
+
+Note that unaligned checkpoints is a brand-new feature that currently has the
+following limitations:
+
+- You cannot rescale or change job graph with from unaligned checkpoints. You
+ have to take a savepoint before rescaling. Savepoints are always aligned
+ independent of the alignment setting of checkpoints.
+- Flink currently does not support concurrent unaligned checkpoints. However,
+ due to the more predictable and shorter checkpointing times, concurrent
+ checkpoints might not be needed at all. However, savepoints can also not
+ happen concurrently to unaligned checkpoints, so they will take slightly
+ longer.
+- Unaligned checkpoints break with an implicit guarantee in respect to
+ watermarks during recovery:
+
+Currently, Flink generates the watermark as a first step of recovery instead of
+storing the latest watermark in the operators to ease rescaling. In unaligned
+checkpoints, that means on recovery, **Flink generates watermarks after it
+restores in-flight data**. If your pipeline uses an **operator that applies the
+latest watermark on each record**, it will produce **different results** than
+for aligned checkpoints. If your operator depends on the latest watermark being
+always available, then the workaround is to store the watermark in the operator
+state. To support rescaling, watermarks should be stored per key-group in a
+union-state. We most likely will implement this approach as a general solution
+(didn't make it into Flink 1.11.0).
+
+In the upcoming release(s), Flink will address these limitations and will
+provide a fine-grained way to trigger unaligned checkpoints only for the
+in-flight data that moves slowly with timeout mechanism. These options will
+decrease the pressure on I/O in the state backends and eventually allow
+unaligned checkpoints to become the default checkpointing.
+
{% top %}
diff --git a/docs/ops/state/large_state_tuning.md b/docs/ops/state/large_state_tuning.md
index ee9e93c9c7631a..55bf0035cf948d 100644
--- a/docs/ops/state/large_state_tuning.md
+++ b/docs/ops/state/large_state_tuning.md
@@ -278,6 +278,8 @@ that the task-local state is an in-memory consisting of heap objects, and not st
Task-local recovery is *deactivated by default* and can be activated through Flink's configuration with the key `state.backend.local-recovery` as specified
in `CheckpointingOptions.LOCAL_RECOVERY`. The value for this setting can either be *true* to enable or *false* (default) to disable local recovery.
+Note that [unaligned checkpoints]({% link ops/state/checkpoints.md %}#unaligned-checkpoints) currently do not support task-local recovery.
+
### Details on task-local recovery for different state backends
***Limitation**: Currently, task-local recovery only covers keyed state backends. Keyed state is typically by far the largest part of the state. In the near future, we will
diff --git a/docs/ops/state/savepoints.md b/docs/ops/state/savepoints.md
index c235344eebcfe7..bc234509ceb168 100644
--- a/docs/ops/state/savepoints.md
+++ b/docs/ops/state/savepoints.md
@@ -27,7 +27,7 @@ under the License.
## What is a Savepoint? How is a Savepoint different from a Checkpoint?
-A Savepoint is a consistent image of the execution state of a streaming job, created via Flink's [checkpointing mechanism]({{ site.baseurl }}/internals/stream_checkpointing.html). You can use Savepoints to stop-and-resume, fork,
+A Savepoint is a consistent image of the execution state of a streaming job, created via Flink's [checkpointing mechanism]({{ site.baseurl }}/learn-flink/fault_tolerance.html). You can use Savepoints to stop-and-resume, fork,
or update your Flink jobs. Savepoints consist of two parts: a directory with (typically large) binary files on stable storage (e.g. HDFS, S3, ...) and a (relatively small) meta data file. The files on stable storage represent the net data of the job's execution state
image. The meta data file of a Savepoint contains (primarily) pointers to all files on stable storage that are part of the Savepoint, in form of absolute paths.
diff --git a/docs/ops/state/savepoints.zh.md b/docs/ops/state/savepoints.zh.md
index 6bdb9df74a2a64..2981fb732429d9 100644
--- a/docs/ops/state/savepoints.zh.md
+++ b/docs/ops/state/savepoints.zh.md
@@ -27,7 +27,7 @@ under the License.
## 什么是 Savepoint ? Savepoint 与 Checkpoint 有什么不同?
-Savepoint 是依据 Flink [checkpointing 机制]({{ site.baseurl }}/zh/internals/stream_checkpointing.html)所创建的流作业执行状态的一致镜像。 你可以使用 Savepoint 进行 Flink 作业的停止与重启、fork 或者更新。 Savepoint 由两部分组成:稳定存储(列入 HDFS,S3,...) 上包含二进制文件的目录(通常很大),和元数据文件(相对较小)。 稳定存储上的文件表示作业执行状态的数据镜像。 Savepoint 的元数据文件以(绝对路径)的形式包含(主要)指向作为 Savepoint 一部分的稳定存储上的所有文件的指针。
+Savepoint 是依据 Flink [checkpointing 机制]({{ site.baseurl }}/zh/learn-flink/fault_tolerance.html)所创建的流作业执行状态的一致镜像。 你可以使用 Savepoint 进行 Flink 作业的停止与重启、fork 或者更新。 Savepoint 由两部分组成:稳定存储(列入 HDFS,S3,...) 上包含二进制文件的目录(通常很大),和元数据文件(相对较小)。 稳定存储上的文件表示作业执行状态的数据镜像。 Savepoint 的元数据文件以(绝对路径)的形式包含(主要)指向作为 Savepoint 一部分的稳定存储上的所有文件的指针。
注意: 为了允许程序和 Flink 版本之间的升级,请务必查看以下有关分配算子 ID 的部分 。
diff --git a/docs/ops/state/state_backends.md b/docs/ops/state/state_backends.md
index c13c3d64d29f77..f750a5dae3d86e 100644
--- a/docs/ops/state/state_backends.md
+++ b/docs/ops/state/state_backends.md
@@ -54,7 +54,7 @@ The *MemoryStateBackend* holds data internally as objects on the Java heap. Key/
that store the values, triggers, etc.
Upon checkpoints, this state backend will snapshot the state and send it as part of the checkpoint acknowledgement messages to the
-JobManager (master), which stores it on its heap as well.
+JobManager, which stores it on its heap as well.
The MemoryStateBackend can be configured to use asynchronous snapshots. While we strongly encourage the use of asynchronous snapshots to avoid blocking pipelines, please note that this is currently enabled
by default. To disable this feature, users can instantiate a `MemoryStateBackend` with the corresponding boolean flag in the constructor set to `false`(this should only used for debug), e.g.:
@@ -243,7 +243,7 @@ For advanced tuning, Flink also provides two parameters to control the division
Moreover, the L0 level filter and index are pinned into the cache by default to mitigate performance problems,
more details please refer to the [RocksDB-documentation](https://github.com/facebook/rocksdb/wiki/Block-Cache#caching-index-filter-and-compression-dictionary-blocks).
-Note When the above described mechanism (`cache` and `write buffer manager`) is enabled, it will override any customized settings for block caches and write buffers done via [`PredefinedOptions`](#predefined-per-columnfamily-options) and [`OptionsFactory`](#passing-options-factory-to-rocksdb).
+Note When the above described mechanism (`cache` and `write buffer manager`) is enabled, it will override any customized settings for block caches and write buffers done via [`PredefinedOptions`](#predefined-per-columnfamily-options) and [`RocksDBOptionsFactory`](#passing-options-factory-to-rocksdb).
Note *Expert Mode*: To control memory manually, you can set `state.backend.rocksdb.memory.managed` to `false` and configure RocksDB via [`ColumnFamilyOptions`](#passing-options-factory-to-rocksdb). Alternatively, you can use the above mentioned cache/buffer-manager mechanism, but set the memory size to a fixed amount independent of Flink's managed memory size (`state.backend.rocksdb.memory.fixed-per-slot` option). Note that in both cases, users need to ensure on their own that enough memory is available outside the JVM for RocksDB.
@@ -286,7 +286,7 @@ The default value for this option is `DEFAULT` which translates to `PredefinedOp
To manually control RocksDB's options, you need to configure an `RocksDBOptionsFactory`. This mechanism gives you fine-grained control over the settings of the Column Families, for example memory use, thread, compaction settings, etc. There is currently one Column Family per each state in each operator.
-There are two ways to pass an OptionsFactory to the RocksDB State Backend:
+There are two ways to pass a RocksDBOptionsFactory to the RocksDB State Backend:
- Configure options factory class name in the `flink-conf.yaml` via `state.backend.rocksdb.options-factory`.
@@ -302,7 +302,7 @@ allocating more memory than configured.
**Reading Column Family Options from flink-conf.yaml**
-When an `OptionsFactory` implements the `ConfigurableRocksDBOptionsFactory` interface, it can directly read settings from the configuration (`flink-conf.yaml`).
+When a `RocksDBOptionsFactory` implements the `ConfigurableRocksDBOptionsFactory` interface, it can directly read settings from the configuration (`flink-conf.yaml`).
The default value for `state.backend.rocksdb.options-factory` is in fact `org.apache.flink.contrib.streaming.state.DefaultConfigurableOptionsFactory` which picks up all config options [defined here]({{ site.baseurl }}/ops/config.html#advanced-rocksdb-state-backends-options) by default. Hence, you can configure low-level Column Family options simply by turning off managed memory for RocksDB and putting the relevant entries in the configuration.
@@ -331,7 +331,7 @@ public class MyOptionsFactory implements ConfigurableRocksDBOptionsFactory {
}
@Override
- public OptionsFactory configure(Configuration configuration) {
+ public RocksDBOptionsFactory configure(Configuration configuration) {
this.blockCacheSize =
configuration.getLong("my.custom.rocksdb.block.cache.size", DEFAULT_SIZE);
return this;
diff --git a/docs/ops/state/state_backends.zh.md b/docs/ops/state/state_backends.zh.md
index 59b54558e5106c..89744f6f784138 100644
--- a/docs/ops/state/state_backends.zh.md
+++ b/docs/ops/state/state_backends.zh.md
@@ -94,6 +94,8 @@ FsStateBackend 适用场景:
建议同时将 [managed memory](../memory/mem_setup_tm.html#managed-memory) 设为0,以保证将最大限度的内存分配给 JVM 上的用户代码。
+
+
### RocksDBStateBackend
*RocksDBStateBackend* 需要配置一个文件系统的 URL (类型、地址、路径),例如:"hdfs://namenode:40010/flink/checkpoints" 或 "file:///data/flink/checkpoints"。
@@ -231,7 +233,7 @@ Flink还提供了两个参数来控制*写路径*(MemTable)和*读路径*(
- `state.backend.rocksdb.memory.high-prio-pool-ratio`,默认值 `0.1`,即 10% 的 block cache 内存会优先分配给索引及过滤器。
我们强烈建议不要将此值设置为零,以防止索引和过滤器被频繁踢出缓存而导致性能问题。此外,我们默认将L0级的过滤器和索引将被固定到缓存中以提高性能,更多详细信息请参阅 [RocksDB 文档](https://github.com/facebook/rocksdb/wiki/Block-Cache#caching-index-filter-and-compression-dictionary-blocks)。
-注意 上述机制开启时将覆盖用户在 [`PredefinedOptions`](#predefined-per-columnfamily-options) 和 [`OptionsFactory`](#passing-options-factory-to-rocksdb) 中对 block cache 和 write buffer 进行的配置。
+注意 上述机制开启时将覆盖用户在 [`PredefinedOptions`](#predefined-per-columnfamily-options) 和 [`RocksDBOptionsFactory`](#passing-options-factory-to-rocksdb) 中对 block cache 和 write buffer 进行的配置。
注意 *仅面向专业用户*:若要手动控制内存,可以将 `state.backend.rocksdb.memory.managed` 设置为 `false`,并通过 [`ColumnFamilyOptions`](#passing-options-factory-to-rocksdb) 配置 RocksDB。
或者可以复用上述 cache/write-buffer-manager 机制,但将内存大小设置为与 Flink 的托管内存大小无关的固定大小(通过 `state.backend.rocksdb.memory.fixed-per-slot` 选项)。
@@ -318,7 +320,7 @@ public class MyOptionsFactory implements ConfigurableRocksDBOptionsFactory {
}
@Override
- public OptionsFactory configure(Configuration configuration) {
+ public RocksDBOptionsFactory configure(Configuration configuration) {
this.blockCacheSize =
configuration.getLong("my.custom.rocksdb.block.cache.size", DEFAULT_SIZE);
return this;
diff --git a/docs/page/js/flink.js b/docs/page/js/flink.js
index 885a8ffad49b9f..5d744e70c600cd 100644
--- a/docs/page/js/flink.js
+++ b/docs/page/js/flink.js
@@ -123,5 +123,5 @@ $(function() {
// Scroll now too in case we had opened the page on a hash, but wait a bit because some browsers
// will try to do *their* initial scroll after running the onReady handler.
- $(window).load(function() { setTimeout(function() { maybeScrollToHash(); }, 25); });
+ $(window).ready(function() { setTimeout(function() { maybeScrollToHash(); }, 25); });
});
diff --git a/docs/page/js/jquery.min.js b/docs/page/js/jquery.min.js
index e6a051d0d1d327..b0614034ad3a95 100644
--- a/docs/page/js/jquery.min.js
+++ b/docs/page/js/jquery.min.js
@@ -1,4 +1,2 @@
-/*! jQuery v1.11.2 | (c) 2005, 2014 jQuery Foundation, Inc. | jquery.org/license */
-!function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){var c=[],d=c.slice,e=c.concat,f=c.push,g=c.indexOf,h={},i=h.toString,j=h.hasOwnProperty,k={},l="1.11.2",m=function(a,b){return new m.fn.init(a,b)},n=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,o=/^-ms-/,p=/-([\da-z])/gi,q=function(a,b){return b.toUpperCase()};m.fn=m.prototype={jquery:l,constructor:m,selector:"",length:0,toArray:function(){return d.call(this)},get:function(a){return null!=a?0>a?this[a+this.length]:this[a]:d.call(this)},pushStack:function(a){var b=m.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a,b){return m.each(this,a,b)},map:function(a){return this.pushStack(m.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(d.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor(null)},push:f,sort:c.sort,splice:c.splice},m.extend=m.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||m.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(e=arguments[h]))for(d in e)a=g[d],c=e[d],g!==c&&(j&&c&&(m.isPlainObject(c)||(b=m.isArray(c)))?(b?(b=!1,f=a&&m.isArray(a)?a:[]):f=a&&m.isPlainObject(a)?a:{},g[d]=m.extend(j,f,c)):void 0!==c&&(g[d]=c));return g},m.extend({expando:"jQuery"+(l+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===m.type(a)},isArray:Array.isArray||function(a){return"array"===m.type(a)},isWindow:function(a){return null!=a&&a==a.window},isNumeric:function(a){return!m.isArray(a)&&a-parseFloat(a)+1>=0},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},isPlainObject:function(a){var b;if(!a||"object"!==m.type(a)||a.nodeType||m.isWindow(a))return!1;try{if(a.constructor&&!j.call(a,"constructor")&&!j.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}if(k.ownLast)for(b in a)return j.call(a,b);for(b in a);return void 0===b||j.call(a,b)},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?h[i.call(a)]||"object":typeof a},globalEval:function(b){b&&m.trim(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(o,"ms-").replace(p,q)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b,c){var d,e=0,f=a.length,g=r(a);if(c){if(g){for(;f>e;e++)if(d=b.apply(a[e],c),d===!1)break}else for(e in a)if(d=b.apply(a[e],c),d===!1)break}else if(g){for(;f>e;e++)if(d=b.call(a[e],e,a[e]),d===!1)break}else for(e in a)if(d=b.call(a[e],e,a[e]),d===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(n,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(r(Object(a))?m.merge(c,"string"==typeof a?[a]:a):f.call(c,a)),c},inArray:function(a,b,c){var d;if(b){if(g)return g.call(b,a,c);for(d=b.length,c=c?0>c?Math.max(0,d+c):c:0;d>c;c++)if(c in b&&b[c]===a)return c}return-1},merge:function(a,b){var c=+b.length,d=0,e=a.length;while(c>d)a[e++]=b[d++];if(c!==c)while(void 0!==b[d])a[e++]=b[d++];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,f=0,g=a.length,h=r(a),i=[];if(h)for(;g>f;f++)d=b(a[f],f,c),null!=d&&i.push(d);else for(f in a)d=b(a[f],f,c),null!=d&&i.push(d);return e.apply([],i)},guid:1,proxy:function(a,b){var c,e,f;return"string"==typeof b&&(f=a[b],b=a,a=f),m.isFunction(a)?(c=d.call(arguments,2),e=function(){return a.apply(b||this,c.concat(d.call(arguments)))},e.guid=a.guid=a.guid||m.guid++,e):void 0},now:function(){return+new Date},support:k}),m.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(a,b){h["[object "+b+"]"]=b.toLowerCase()});function r(a){var b=a.length,c=m.type(a);return"function"===c||m.isWindow(a)?!1:1===a.nodeType&&b?!0:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var s=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=hb(),z=hb(),A=hb(),B=function(a,b){return a===b&&(l=!0),0},C=1<<31,D={}.hasOwnProperty,E=[],F=E.pop,G=E.push,H=E.push,I=E.slice,J=function(a,b){for(var c=0,d=a.length;d>c;c++)if(a[c]===b)return c;return-1},K="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",L="[\\x20\\t\\r\\n\\f]",M="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",N=M.replace("w","w#"),O="\\["+L+"*("+M+")(?:"+L+"*([*^$|!~]?=)"+L+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+N+"))|)"+L+"*\\]",P=":("+M+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+O+")*)|.*)\\)|)",Q=new RegExp(L+"+","g"),R=new RegExp("^"+L+"+|((?:^|[^\\\\])(?:\\\\.)*)"+L+"+$","g"),S=new RegExp("^"+L+"*,"+L+"*"),T=new RegExp("^"+L+"*([>+~]|"+L+")"+L+"*"),U=new RegExp("="+L+"*([^\\]'\"]*?)"+L+"*\\]","g"),V=new RegExp(P),W=new RegExp("^"+N+"$"),X={ID:new RegExp("^#("+M+")"),CLASS:new RegExp("^\\.("+M+")"),TAG:new RegExp("^("+M.replace("w","w*")+")"),ATTR:new RegExp("^"+O),PSEUDO:new RegExp("^"+P),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+L+"*(even|odd|(([+-]|)(\\d*)n|)"+L+"*(?:([+-]|)"+L+"*(\\d+)|))"+L+"*\\)|)","i"),bool:new RegExp("^(?:"+K+")$","i"),needsContext:new RegExp("^"+L+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+L+"*((?:-\\d)?\\d*)"+L+"*\\)|)(?=[^-]|$)","i")},Y=/^(?:input|select|textarea|button)$/i,Z=/^h\d$/i,$=/^[^{]+\{\s*\[native \w/,_=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ab=/[+~]/,bb=/'|\\/g,cb=new RegExp("\\\\([\\da-f]{1,6}"+L+"?|("+L+")|.)","ig"),db=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:0>d?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)},eb=function(){m()};try{H.apply(E=I.call(v.childNodes),v.childNodes),E[v.childNodes.length].nodeType}catch(fb){H={apply:E.length?function(a,b){G.apply(a,I.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function gb(a,b,d,e){var f,h,j,k,l,o,r,s,w,x;if((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,d=d||[],k=b.nodeType,"string"!=typeof a||!a||1!==k&&9!==k&&11!==k)return d;if(!e&&p){if(11!==k&&(f=_.exec(a)))if(j=f[1]){if(9===k){if(h=b.getElementById(j),!h||!h.parentNode)return d;if(h.id===j)return d.push(h),d}else if(b.ownerDocument&&(h=b.ownerDocument.getElementById(j))&&t(b,h)&&h.id===j)return d.push(h),d}else{if(f[2])return H.apply(d,b.getElementsByTagName(a)),d;if((j=f[3])&&c.getElementsByClassName)return H.apply(d,b.getElementsByClassName(j)),d}if(c.qsa&&(!q||!q.test(a))){if(s=r=u,w=b,x=1!==k&&a,1===k&&"object"!==b.nodeName.toLowerCase()){o=g(a),(r=b.getAttribute("id"))?s=r.replace(bb,"\\$&"):b.setAttribute("id",s),s="[id='"+s+"'] ",l=o.length;while(l--)o[l]=s+rb(o[l]);w=ab.test(a)&&pb(b.parentNode)||b,x=o.join(",")}if(x)try{return H.apply(d,w.querySelectorAll(x)),d}catch(y){}finally{r||b.removeAttribute("id")}}}return i(a.replace(R,"$1"),b,d,e)}function hb(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function ib(a){return a[u]=!0,a}function jb(a){var b=n.createElement("div");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function kb(a,b){var c=a.split("|"),e=a.length;while(e--)d.attrHandle[c[e]]=b}function lb(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&(~b.sourceIndex||C)-(~a.sourceIndex||C);if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function mb(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function nb(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function ob(a){return ib(function(b){return b=+b,ib(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function pb(a){return a&&"undefined"!=typeof a.getElementsByTagName&&a}c=gb.support={},f=gb.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?"HTML"!==b.nodeName:!1},m=gb.setDocument=function(a){var b,e,g=a?a.ownerDocument||a:v;return g!==n&&9===g.nodeType&&g.documentElement?(n=g,o=g.documentElement,e=g.defaultView,e&&e!==e.top&&(e.addEventListener?e.addEventListener("unload",eb,!1):e.attachEvent&&e.attachEvent("onunload",eb)),p=!f(g),c.attributes=jb(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=jb(function(a){return a.appendChild(g.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=$.test(g.getElementsByClassName),c.getById=jb(function(a){return o.appendChild(a).id=u,!g.getElementsByName||!g.getElementsByName(u).length}),c.getById?(d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c=b.getElementById(a);return c&&c.parentNode?[c]:[]}},d.filter.ID=function(a){var b=a.replace(cb,db);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(cb,db);return function(a){var c="undefined"!=typeof a.getAttributeNode&&a.getAttributeNode("id");return c&&c.value===b}}),d.find.TAG=c.getElementsByTagName?function(a,b){return"undefined"!=typeof b.getElementsByTagName?b.getElementsByTagName(a):c.qsa?b.querySelectorAll(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){return p?b.getElementsByClassName(a):void 0},r=[],q=[],(c.qsa=$.test(g.querySelectorAll))&&(jb(function(a){o.appendChild(a).innerHTML="",a.querySelectorAll("[msallowcapture^='']").length&&q.push("[*^$]="+L+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+L+"*(?:value|"+K+")"),a.querySelectorAll("[id~="+u+"-]").length||q.push("~="),a.querySelectorAll(":checked").length||q.push(":checked"),a.querySelectorAll("a#"+u+"+*").length||q.push(".#.+[+~]")}),jb(function(a){var b=g.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+L+"*[*^$|!~]?="),a.querySelectorAll(":enabled").length||q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=$.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&jb(function(a){c.disconnectedMatch=s.call(a,"div"),s.call(a,"[s!='']:x"),r.push("!=",P)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=$.test(o.compareDocumentPosition),t=b||$.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===g||a.ownerDocument===v&&t(v,a)?-1:b===g||b.ownerDocument===v&&t(v,b)?1:k?J(k,a)-J(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,e=a.parentNode,f=b.parentNode,h=[a],i=[b];if(!e||!f)return a===g?-1:b===g?1:e?-1:f?1:k?J(k,a)-J(k,b):0;if(e===f)return lb(a,b);c=a;while(c=c.parentNode)h.unshift(c);c=b;while(c=c.parentNode)i.unshift(c);while(h[d]===i[d])d++;return d?lb(h[d],i[d]):h[d]===v?-1:i[d]===v?1:0},g):n},gb.matches=function(a,b){return gb(a,null,null,b)},gb.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(U,"='$1']"),!(!c.matchesSelector||!p||r&&r.test(b)||q&&q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return gb(b,n,null,[a]).length>0},gb.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},gb.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&D.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},gb.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},gb.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=gb.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=gb.selectors={cacheLength:50,createPseudo:ib,match:X,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(cb,db),a[3]=(a[3]||a[4]||a[5]||"").replace(cb,db),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||gb.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&gb.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return X.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&V.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(cb,db).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+L+")"+a+"("+L+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||"undefined"!=typeof a.getAttribute&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=gb.attr(d,a);return null==e?"!="===b:b?(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e.replace(Q," ")+" ").indexOf(c)>-1:"|="===b?e===c||e.slice(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h;if(q){if(f){while(p){l=b;while(l=l[p])if(h?l.nodeName.toLowerCase()===r:1===l.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){k=q[u]||(q[u]={}),j=k[a]||[],n=j[0]===w&&j[1],m=j[0]===w&&j[2],l=n&&q.childNodes[n];while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if(1===l.nodeType&&++m&&l===b){k[a]=[w,n,m];break}}else if(s&&(j=(b[u]||(b[u]={}))[a])&&j[0]===w)m=j[1];else while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if((h?l.nodeName.toLowerCase()===r:1===l.nodeType)&&++m&&(s&&((l[u]||(l[u]={}))[a]=[w,m]),l===b))break;return m-=e,m===d||m%d===0&&m/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||gb.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?ib(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=J(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:ib(function(a){var b=[],c=[],d=h(a.replace(R,"$1"));return d[u]?ib(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),b[0]=null,!c.pop()}}),has:ib(function(a){return function(b){return gb(a,b).length>0}}),contains:ib(function(a){return a=a.replace(cb,db),function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:ib(function(a){return W.test(a||"")||gb.error("unsupported lang: "+a),a=a.replace(cb,db).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return Z.test(a.nodeName)},input:function(a){return Y.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:ob(function(){return[0]}),last:ob(function(a,b){return[b-1]}),eq:ob(function(a,b,c){return[0>c?c+b:c]}),even:ob(function(a,b){for(var c=0;b>c;c+=2)a.push(c);return a}),odd:ob(function(a,b){for(var c=1;b>c;c+=2)a.push(c);return a}),lt:ob(function(a,b,c){for(var d=0>c?c+b:c;--d>=0;)a.push(d);return a}),gt:ob(function(a,b,c){for(var d=0>c?c+b:c;++db;b++)d+=a[b].value;return d}function sb(a,b,c){var d=b.dir,e=c&&"parentNode"===d,f=x++;return b.first?function(b,c,f){while(b=b[d])if(1===b.nodeType||e)return a(b,c,f)}:function(b,c,g){var h,i,j=[w,f];if(g){while(b=b[d])if((1===b.nodeType||e)&&a(b,c,g))return!0}else while(b=b[d])if(1===b.nodeType||e){if(i=b[u]||(b[u]={}),(h=i[d])&&h[0]===w&&h[1]===f)return j[2]=h[2];if(i[d]=j,j[2]=a(b,c,g))return!0}}}function tb(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function ub(a,b,c){for(var d=0,e=b.length;e>d;d++)gb(a,b[d],c);return c}function vb(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;i>h;h++)(f=a[h])&&(!c||c(f,d,e))&&(g.push(f),j&&b.push(h));return g}function wb(a,b,c,d,e,f){return d&&!d[u]&&(d=wb(d)),e&&!e[u]&&(e=wb(e,f)),ib(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||ub(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:vb(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=vb(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?J(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=vb(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):H.apply(g,r)})}function xb(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=sb(function(a){return a===b},h,!0),l=sb(function(a){return J(b,a)>-1},h,!0),m=[function(a,c,d){var e=!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d));return b=null,e}];f>i;i++)if(c=d.relative[a[i].type])m=[sb(tb(m),c)];else{if(c=d.filter[a[i].type].apply(null,a[i].matches),c[u]){for(e=++i;f>e;e++)if(d.relative[a[e].type])break;return wb(i>1&&tb(m),i>1&&rb(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(R,"$1"),c,e>i&&xb(a.slice(i,e)),f>e&&xb(a=a.slice(e)),f>e&&rb(a))}m.push(c)}return tb(m)}function yb(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,h,i,k){var l,m,o,p=0,q="0",r=f&&[],s=[],t=j,u=f||e&&d.find.TAG("*",k),v=w+=null==t?1:Math.random()||.1,x=u.length;for(k&&(j=g!==n&&g);q!==x&&null!=(l=u[q]);q++){if(e&&l){m=0;while(o=a[m++])if(o(l,g,h)){i.push(l);break}k&&(w=v)}c&&((l=!o&&l)&&p--,f&&r.push(l))}if(p+=q,c&&q!==p){m=0;while(o=b[m++])o(r,s,g,h);if(f){if(p>0)while(q--)r[q]||s[q]||(s[q]=F.call(i));s=vb(s)}H.apply(i,s),k&&!f&&s.length>0&&p+b.length>1&&gb.uniqueSort(i)}return k&&(w=v,j=t),r};return c?ib(f):f}return h=gb.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=xb(b[c]),f[u]?d.push(f):e.push(f);f=A(a,yb(e,d)),f.selector=a}return f},i=gb.select=function(a,b,e,f){var i,j,k,l,m,n="function"==typeof a&&a,o=!f&&g(a=n.selector||a);if(e=e||[],1===o.length){if(j=o[0]=o[0].slice(0),j.length>2&&"ID"===(k=j[0]).type&&c.getById&&9===b.nodeType&&p&&d.relative[j[1].type]){if(b=(d.find.ID(k.matches[0].replace(cb,db),b)||[])[0],!b)return e;n&&(b=b.parentNode),a=a.slice(j.shift().value.length)}i=X.needsContext.test(a)?0:j.length;while(i--){if(k=j[i],d.relative[l=k.type])break;if((m=d.find[l])&&(f=m(k.matches[0].replace(cb,db),ab.test(j[0].type)&&pb(b.parentNode)||b))){if(j.splice(i,1),a=f.length&&rb(j),!a)return H.apply(e,f),e;break}}}return(n||h(a,o))(f,b,!p,e,ab.test(a)&&pb(b.parentNode)||b),e},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=jb(function(a){return 1&a.compareDocumentPosition(n.createElement("div"))}),jb(function(a){return a.innerHTML="","#"===a.firstChild.getAttribute("href")})||kb("type|href|height|width",function(a,b,c){return c?void 0:a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&jb(function(a){return a.innerHTML="",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||kb("value",function(a,b,c){return c||"input"!==a.nodeName.toLowerCase()?void 0:a.defaultValue}),jb(function(a){return null==a.getAttribute("disabled")})||kb(K,function(a,b,c){var d;return c?void 0:a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),gb}(a);m.find=s,m.expr=s.selectors,m.expr[":"]=m.expr.pseudos,m.unique=s.uniqueSort,m.text=s.getText,m.isXMLDoc=s.isXML,m.contains=s.contains;var t=m.expr.match.needsContext,u=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,v=/^.[^:#\[\.,]*$/;function w(a,b,c){if(m.isFunction(b))return m.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return m.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(v.test(b))return m.filter(b,a,c);b=m.filter(b,a)}return m.grep(a,function(a){return m.inArray(a,b)>=0!==c})}m.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?m.find.matchesSelector(d,a)?[d]:[]:m.find.matches(a,m.grep(b,function(a){return 1===a.nodeType}))},m.fn.extend({find:function(a){var b,c=[],d=this,e=d.length;if("string"!=typeof a)return this.pushStack(m(a).filter(function(){for(b=0;e>b;b++)if(m.contains(d[b],this))return!0}));for(b=0;e>b;b++)m.find(a,d[b],c);return c=this.pushStack(e>1?m.unique(c):c),c.selector=this.selector?this.selector+" "+a:a,c},filter:function(a){return this.pushStack(w(this,a||[],!1))},not:function(a){return this.pushStack(w(this,a||[],!0))},is:function(a){return!!w(this,"string"==typeof a&&t.test(a)?m(a):a||[],!1).length}});var x,y=a.document,z=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,A=m.fn.init=function(a,b){var c,d;if(!a)return this;if("string"==typeof a){if(c="<"===a.charAt(0)&&">"===a.charAt(a.length-1)&&a.length>=3?[null,a,null]:z.exec(a),!c||!c[1]&&b)return!b||b.jquery?(b||x).find(a):this.constructor(b).find(a);if(c[1]){if(b=b instanceof m?b[0]:b,m.merge(this,m.parseHTML(c[1],b&&b.nodeType?b.ownerDocument||b:y,!0)),u.test(c[1])&&m.isPlainObject(b))for(c in b)m.isFunction(this[c])?this[c](b[c]):this.attr(c,b[c]);return this}if(d=y.getElementById(c[2]),d&&d.parentNode){if(d.id!==c[2])return x.find(a);this.length=1,this[0]=d}return this.context=y,this.selector=a,this}return a.nodeType?(this.context=this[0]=a,this.length=1,this):m.isFunction(a)?"undefined"!=typeof x.ready?x.ready(a):a(m):(void 0!==a.selector&&(this.selector=a.selector,this.context=a.context),m.makeArray(a,this))};A.prototype=m.fn,x=m(y);var B=/^(?:parents|prev(?:Until|All))/,C={children:!0,contents:!0,next:!0,prev:!0};m.extend({dir:function(a,b,c){var d=[],e=a[b];while(e&&9!==e.nodeType&&(void 0===c||1!==e.nodeType||!m(e).is(c)))1===e.nodeType&&d.push(e),e=e[b];return d},sibling:function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c}}),m.fn.extend({has:function(a){var b,c=m(a,this),d=c.length;return this.filter(function(){for(b=0;d>b;b++)if(m.contains(this,c[b]))return!0})},closest:function(a,b){for(var c,d=0,e=this.length,f=[],g=t.test(a)||"string"!=typeof a?m(a,b||this.context):0;e>d;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&m.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?m.unique(f):f)},index:function(a){return a?"string"==typeof a?m.inArray(this[0],m(a)):m.inArray(a.jquery?a[0]:a,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(m.unique(m.merge(this.get(),m(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function D(a,b){do a=a[b];while(a&&1!==a.nodeType);return a}m.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return m.dir(a,"parentNode")},parentsUntil:function(a,b,c){return m.dir(a,"parentNode",c)},next:function(a){return D(a,"nextSibling")},prev:function(a){return D(a,"previousSibling")},nextAll:function(a){return m.dir(a,"nextSibling")},prevAll:function(a){return m.dir(a,"previousSibling")},nextUntil:function(a,b,c){return m.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return m.dir(a,"previousSibling",c)},siblings:function(a){return m.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return m.sibling(a.firstChild)},contents:function(a){return m.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:m.merge([],a.childNodes)}},function(a,b){m.fn[a]=function(c,d){var e=m.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=m.filter(d,e)),this.length>1&&(C[a]||(e=m.unique(e)),B.test(a)&&(e=e.reverse())),this.pushStack(e)}});var E=/\S+/g,F={};function G(a){var b=F[a]={};return m.each(a.match(E)||[],function(a,c){b[c]=!0}),b}m.Callbacks=function(a){a="string"==typeof a?F[a]||G(a):m.extend({},a);var b,c,d,e,f,g,h=[],i=!a.once&&[],j=function(l){for(c=a.memory&&l,d=!0,f=g||0,g=0,e=h.length,b=!0;h&&e>f;f++)if(h[f].apply(l[0],l[1])===!1&&a.stopOnFalse){c=!1;break}b=!1,h&&(i?i.length&&j(i.shift()):c?h=[]:k.disable())},k={add:function(){if(h){var d=h.length;!function f(b){m.each(b,function(b,c){var d=m.type(c);"function"===d?a.unique&&k.has(c)||h.push(c):c&&c.length&&"string"!==d&&f(c)})}(arguments),b?e=h.length:c&&(g=d,j(c))}return this},remove:function(){return h&&m.each(arguments,function(a,c){var d;while((d=m.inArray(c,h,d))>-1)h.splice(d,1),b&&(e>=d&&e--,f>=d&&f--)}),this},has:function(a){return a?m.inArray(a,h)>-1:!(!h||!h.length)},empty:function(){return h=[],e=0,this},disable:function(){return h=i=c=void 0,this},disabled:function(){return!h},lock:function(){return i=void 0,c||k.disable(),this},locked:function(){return!i},fireWith:function(a,c){return!h||d&&!i||(c=c||[],c=[a,c.slice?c.slice():c],b?i.push(c):j(c)),this},fire:function(){return k.fireWith(this,arguments),this},fired:function(){return!!d}};return k},m.extend({Deferred:function(a){var b=[["resolve","done",m.Callbacks("once memory"),"resolved"],["reject","fail",m.Callbacks("once memory"),"rejected"],["notify","progress",m.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return m.Deferred(function(c){m.each(b,function(b,f){var g=m.isFunction(a[b])&&a[b];e[f[1]](function(){var a=g&&g.apply(this,arguments);a&&m.isFunction(a.promise)?a.promise().done(c.resolve).fail(c.reject).progress(c.notify):c[f[0]+"With"](this===d?c.promise():this,g?[a]:arguments)})}),a=null}).promise()},promise:function(a){return null!=a?m.extend(a,d):d}},e={};return d.pipe=d.then,m.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[1^a][2].disable,b[2][2].lock),e[f[0]]=function(){return e[f[0]+"With"](this===e?d:this,arguments),this},e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=d.call(arguments),e=c.length,f=1!==e||a&&m.isFunction(a.promise)?e:0,g=1===f?a:m.Deferred(),h=function(a,b,c){return function(e){b[a]=this,c[a]=arguments.length>1?d.call(arguments):e,c===i?g.notifyWith(b,c):--f||g.resolveWith(b,c)}},i,j,k;if(e>1)for(i=new Array(e),j=new Array(e),k=new Array(e);e>b;b++)c[b]&&m.isFunction(c[b].promise)?c[b].promise().done(h(b,k,c)).fail(g.reject).progress(h(b,j,i)):--f;return f||g.resolveWith(k,c),g.promise()}});var H;m.fn.ready=function(a){return m.ready.promise().done(a),this},m.extend({isReady:!1,readyWait:1,holdReady:function(a){a?m.readyWait++:m.ready(!0)},ready:function(a){if(a===!0?!--m.readyWait:!m.isReady){if(!y.body)return setTimeout(m.ready);m.isReady=!0,a!==!0&&--m.readyWait>0||(H.resolveWith(y,[m]),m.fn.triggerHandler&&(m(y).triggerHandler("ready"),m(y).off("ready")))}}});function I(){y.addEventListener?(y.removeEventListener("DOMContentLoaded",J,!1),a.removeEventListener("load",J,!1)):(y.detachEvent("onreadystatechange",J),a.detachEvent("onload",J))}function J(){(y.addEventListener||"load"===event.type||"complete"===y.readyState)&&(I(),m.ready())}m.ready.promise=function(b){if(!H)if(H=m.Deferred(),"complete"===y.readyState)setTimeout(m.ready);else if(y.addEventListener)y.addEventListener("DOMContentLoaded",J,!1),a.addEventListener("load",J,!1);else{y.attachEvent("onreadystatechange",J),a.attachEvent("onload",J);var c=!1;try{c=null==a.frameElement&&y.documentElement}catch(d){}c&&c.doScroll&&!function e(){if(!m.isReady){try{c.doScroll("left")}catch(a){return setTimeout(e,50)}I(),m.ready()}}()}return H.promise(b)};var K="undefined",L;for(L in m(k))break;k.ownLast="0"!==L,k.inlineBlockNeedsLayout=!1,m(function(){var a,b,c,d;c=y.getElementsByTagName("body")[0],c&&c.style&&(b=y.createElement("div"),d=y.createElement("div"),d.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",c.appendChild(d).appendChild(b),typeof b.style.zoom!==K&&(b.style.cssText="display:inline;margin:0;border:0;padding:1px;width:1px;zoom:1",k.inlineBlockNeedsLayout=a=3===b.offsetWidth,a&&(c.style.zoom=1)),c.removeChild(d))}),function(){var a=y.createElement("div");if(null==k.deleteExpando){k.deleteExpando=!0;try{delete a.test}catch(b){k.deleteExpando=!1}}a=null}(),m.acceptData=function(a){var b=m.noData[(a.nodeName+" ").toLowerCase()],c=+a.nodeType||1;return 1!==c&&9!==c?!1:!b||b!==!0&&a.getAttribute("classid")===b};var M=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,N=/([A-Z])/g;function O(a,b,c){if(void 0===c&&1===a.nodeType){var d="data-"+b.replace(N,"-$1").toLowerCase();if(c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:M.test(c)?m.parseJSON(c):c}catch(e){}m.data(a,b,c)}else c=void 0}return c}function P(a){var b;for(b in a)if(("data"!==b||!m.isEmptyObject(a[b]))&&"toJSON"!==b)return!1;
-return!0}function Q(a,b,d,e){if(m.acceptData(a)){var f,g,h=m.expando,i=a.nodeType,j=i?m.cache:a,k=i?a[h]:a[h]&&h;if(k&&j[k]&&(e||j[k].data)||void 0!==d||"string"!=typeof b)return k||(k=i?a[h]=c.pop()||m.guid++:h),j[k]||(j[k]=i?{}:{toJSON:m.noop}),("object"==typeof b||"function"==typeof b)&&(e?j[k]=m.extend(j[k],b):j[k].data=m.extend(j[k].data,b)),g=j[k],e||(g.data||(g.data={}),g=g.data),void 0!==d&&(g[m.camelCase(b)]=d),"string"==typeof b?(f=g[b],null==f&&(f=g[m.camelCase(b)])):f=g,f}}function R(a,b,c){if(m.acceptData(a)){var d,e,f=a.nodeType,g=f?m.cache:a,h=f?a[m.expando]:m.expando;if(g[h]){if(b&&(d=c?g[h]:g[h].data)){m.isArray(b)?b=b.concat(m.map(b,m.camelCase)):b in d?b=[b]:(b=m.camelCase(b),b=b in d?[b]:b.split(" ")),e=b.length;while(e--)delete d[b[e]];if(c?!P(d):!m.isEmptyObject(d))return}(c||(delete g[h].data,P(g[h])))&&(f?m.cleanData([a],!0):k.deleteExpando||g!=g.window?delete g[h]:g[h]=null)}}}m.extend({cache:{},noData:{"applet ":!0,"embed ":!0,"object ":"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(a){return a=a.nodeType?m.cache[a[m.expando]]:a[m.expando],!!a&&!P(a)},data:function(a,b,c){return Q(a,b,c)},removeData:function(a,b){return R(a,b)},_data:function(a,b,c){return Q(a,b,c,!0)},_removeData:function(a,b){return R(a,b,!0)}}),m.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=m.data(f),1===f.nodeType&&!m._data(f,"parsedAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=m.camelCase(d.slice(5)),O(f,d,e[d])));m._data(f,"parsedAttrs",!0)}return e}return"object"==typeof a?this.each(function(){m.data(this,a)}):arguments.length>1?this.each(function(){m.data(this,a,b)}):f?O(f,a,m.data(f,a)):void 0},removeData:function(a){return this.each(function(){m.removeData(this,a)})}}),m.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=m._data(a,b),c&&(!d||m.isArray(c)?d=m._data(a,b,m.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=m.queue(a,b),d=c.length,e=c.shift(),f=m._queueHooks(a,b),g=function(){m.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return m._data(a,c)||m._data(a,c,{empty:m.Callbacks("once memory").add(function(){m._removeData(a,b+"queue"),m._removeData(a,c)})})}}),m.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.lengthh;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f},W=/^(?:checkbox|radio)$/i;!function(){var a=y.createElement("input"),b=y.createElement("div"),c=y.createDocumentFragment();if(b.innerHTML="
"],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n",""]);var me=/<|?\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d\s*$/g;function qe(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function Le(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function He(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Oe(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var Ut,Xt=[],Vt=/(=)\?(?=&|$)|\?\?/;S.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Xt.pop()||S.expando+"_"+Ct.guid++;return this[e]=!0,e}}),S.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Vt.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Vt.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Vt,"$1"+r):!1!==e.jsonp&&(e.url+=(Et.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||S.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,Xt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((Ut=E.implementation.createHTMLDocument("").body).innerHTML="",2===Ut.childNodes.length),S.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,"position"),c=S(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=S.css(e,"top"),u=S.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):("number"==typeof f.top&&(f.top+="px"),"number"==typeof f.left&&(f.left+="px"),c.css(f))}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===S.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===S.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,"borderTopWidth",!0),i.left+=S.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-S.css(r,"marginTop",!0),left:t.left-i.left-S.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===S.css(e,"position"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;S.fn[t]=function(e){return $(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each(["top","left"],function(e,n){S.cssHooks[n]=$e(y.pixelPosition,function(e,t){if(t)return t=Be(e,n),Me.test(t)?S(e).position()[n]+"px":t})}),S.each({Height:"height",Width:"width"},function(a,s){S.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return $(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){S.fn[n]=function(e,t){return 0
diff --git a/docs/redirects/dependencies.md b/docs/redirects/dependencies.md
index 26debf6e952a36..79848342d30813 100644
--- a/docs/redirects/dependencies.md
+++ b/docs/redirects/dependencies.md
@@ -1,7 +1,7 @@
---
title: "Configuring Dependencies, Connectors, Libraries"
layout: redirect
-redirect: /dev/projectsetup/dependencies.html
+permalink: /dev/project-configuration.html
permalink: /start/dependencies.html
---
diff --git a/docs/redirects/getting-started-dependencies.md b/docs/redirects/getting-started-dependencies.md
new file mode 100644
index 00000000000000..072a4da7ba49d8
--- /dev/null
+++ b/docs/redirects/getting-started-dependencies.md
@@ -0,0 +1,24 @@
+---
+title: Configuring Dependencies, Connectors, Libraries
+layout: redirect
+redirect: /dev/project-configuration.html
+permalink: /getting-started/project-setup/dependencies.html
+---
+
diff --git a/docs/redirects/getting-started.md b/docs/redirects/getting-started.md
new file mode 100644
index 00000000000000..ce76fb25077e8b
--- /dev/null
+++ b/docs/redirects/getting-started.md
@@ -0,0 +1,24 @@
+---
+title: Getting Started
+layout: redirect
+redirect: /index.html
+permalink: /getting-started/index.html
+---
+
diff --git a/docs/redirects/java-quickstart.md b/docs/redirects/java-quickstart.md
new file mode 100644
index 00000000000000..56d7d9082b9896
--- /dev/null
+++ b/docs/redirects/java-quickstart.md
@@ -0,0 +1,24 @@
+---
+title: Java Quckstart
+layout: redirect
+redirect: /dev/project-configuration.html
+permalink: /getting-started/project-setup/java_api_quickstart.html
+---
+
diff --git a/docs/redirects/python_table_walkthrough.md b/docs/redirects/python_table_walkthrough.md
new file mode 100644
index 00000000000000..c23f91ee911637
--- /dev/null
+++ b/docs/redirects/python_table_walkthrough.md
@@ -0,0 +1,24 @@
+---
+title: Python Table Walkthrough
+layout: redirect
+redirect: /try-flink/python_table_api.html
+permalink: /getting-started/walkthroughs/python_table_api.html
+---
+
diff --git a/docs/redirects/scala-quickstart.md b/docs/redirects/scala-quickstart.md
new file mode 100644
index 00000000000000..e8568e6785f0ff
--- /dev/null
+++ b/docs/redirects/scala-quickstart.md
@@ -0,0 +1,24 @@
+---
+title: Java Quckstart
+layout: redirect
+redirect: /dev/project-configuration.html
+permalink: /getting-started/project-setup/scala_api_quickstart.html
+---
+
diff --git a/docs/redirects/scala_quickstart.md b/docs/redirects/scala_quickstart.md
index f50c4dc86b74b6..62a507fd25ac90 100644
--- a/docs/redirects/scala_quickstart.md
+++ b/docs/redirects/scala_quickstart.md
@@ -1,7 +1,7 @@
---
title: "Project Template for Scala"
layout: redirect
-redirect: /dev/projectsetup/scala_api_quickstart.html
+redirect: /dev/project-configuration.html
permalink: /quickstart/scala_api_quickstart.html
---
diff --git a/docs/redirects/training.md b/docs/redirects/training.md
new file mode 100644
index 00000000000000..09cc1296a9f8b8
--- /dev/null
+++ b/docs/redirects/training.md
@@ -0,0 +1,24 @@
+---
+title: Hands-on Training
+layout: redirect
+redirect: /learn-flink/index.html
+permalink: /training/index.html
+---
+
diff --git a/docs/release-notes/flink-1.10.md b/docs/release-notes/flink-1.10.md
index 0e228ec0f500cc..022e0d9b157334 100644
--- a/docs/release-notes/flink-1.10.md
+++ b/docs/release-notes/flink-1.10.md
@@ -464,3 +464,7 @@ blocked. The `MailboxExecutor` can be accessed by using
Interfaces `OptionsFactory` and `ConfigurableOptionsFactory` have been
deprecated in favor of `RocksDBOptionsFactory` and
`ConfigurableRocksDBOptionsFactory`, respectively.
+
+#### Incompatibility of serialized JobGraphs ([FLINK-14594](https://issues.apache.org/jira/browse/FLINK-14594))
+Serialized `JobGraphs` which set the `ResourceSpec` created by Flink versions < `1.10` are no longer compatible with Flink >= `1.10`.
+If you want to migrate these jobs to Flink >= `1.10` you will have to stop the job with a savepoint and then resume it from this savepoint on the Flink >= `1.10` cluster.
diff --git a/docs/release-notes/flink-1.10.zh.md b/docs/release-notes/flink-1.10.zh.md
index 0e228ec0f500cc..022e0d9b157334 100644
--- a/docs/release-notes/flink-1.10.zh.md
+++ b/docs/release-notes/flink-1.10.zh.md
@@ -464,3 +464,7 @@ blocked. The `MailboxExecutor` can be accessed by using
Interfaces `OptionsFactory` and `ConfigurableOptionsFactory` have been
deprecated in favor of `RocksDBOptionsFactory` and
`ConfigurableRocksDBOptionsFactory`, respectively.
+
+#### Incompatibility of serialized JobGraphs ([FLINK-14594](https://issues.apache.org/jira/browse/FLINK-14594))
+Serialized `JobGraphs` which set the `ResourceSpec` created by Flink versions < `1.10` are no longer compatible with Flink >= `1.10`.
+If you want to migrate these jobs to Flink >= `1.10` you will have to stop the job with a savepoint and then resume it from this savepoint on the Flink >= `1.10` cluster.
diff --git a/docs/release-notes/flink-1.11.md b/docs/release-notes/flink-1.11.md
new file mode 100644
index 00000000000000..7d3e67ef8b5c8f
--- /dev/null
+++ b/docs/release-notes/flink-1.11.md
@@ -0,0 +1,303 @@
+---
+title: "Release Notes - Flink 1.11"
+---
+
+
+
+These release notes discuss important aspects, such as configuration, behavior,
+or dependencies, that changed between Flink 1.10 and Flink 1.11. Please read
+these notes carefully if you are planning to upgrade your Flink version to 1.11.
+
+* This will be replaced by the TOC
+{:toc}
+
+### Clusters & Deployment
+#### Support for Application Mode ([FLIP-85](https://cwiki.apache.org/confluence/display/FLINK/FLIP-85+Flink+Application+Mode))
+The user can now submit applications and choose to execute their `main()` method on the cluster rather than the client.
+This allows for more light-weight application submission. For more details,
+see the [Application Mode documentation](https://ci.apache.org/projects/flink/flink-docs-master/ops/deployment/#application-mode).
+
+#### Web Submission behaves the same as detached mode.
+With [FLINK-16657](https://issues.apache.org/jira/browse/FLINK-16657) the web submission logic changes and it exposes
+the same behavior as submitting a job through the CLI in detached mode. This implies that, for instance, jobs based on
+the DataSet API that were using sinks like `print()`, `count()` or `collect()` will now throw an exception while
+before the output was simply never printed. See also comments on related [PR](https://github.com/apache/flink/pull/11460).
+
+#### Support for Hadoop 3.0.0 and higher ([FLINK-11086](https://issues.apache.org/jira/browse/FLINK-11086))
+Flink project does not provide any updated "flink-shaded-hadoop-*" jars.
+Users need to provide Hadoop dependencies through the HADOOP_CLASSPATH environment variable (recommended) or via `lib/` folder.
+Also, the `include-hadoop` Maven profile has been removed.
+
+#### `flink-csv` and `flink-json` are bundled in lib folder ([FLINK-18173](https://issues.apache.org/jira/browse/FLINK-18173))
+There is no need to download manually jar files for `flink-csv` and `flink-json` formats as they are now bundled in the `lib` folder.
+
+#### Removal of `LegacyScheduler` ([FLINK-15629](https://issues.apache.org/jira/browse/FLINK-15629))
+Flink no longer supports the legacy scheduler.
+Hence, setting `jobmanager.scheduler: legacy` will no longer work and fail with an `IllegalArgumentException`.
+The only valid option for `jobmanager.scheduler` is the default value `ng`.
+
+#### Bind user code class loader to lifetime of a slot ([FLINK-16408](https://issues.apache.org/jira/browse/FLINK-16408))
+The user code class loader is being reused by the `TaskExecutor` as long as there is at least a single slot allocated for the respective job.
+This changes Flink's recovery behaviour slightly so that it will not reload static fields.
+The benefit is that this change drastically reduces pressure on the JVM's metaspace.
+
+#### Replaced `slave` file name with `workers` ([FLINK-18307](https://issues.apache.org/jira/browse/FLINK-18307))
+For Standalone Setups, the file with the worker nodes is no longer called `slaves` but `workers`.
+Previous setups that use the `start-cluster.sh` and `stop-cluster.sh` scripts need to rename that file.
+
+#### Flink Docker Integration Improvements
+The examples of `Dockerfiles` and docker image `build.sh` scripts have been removed from [the Flink Github repository](https://github.com/apache/flink). The examples will no longer be maintained by community in the Flink Github repository, including the examples of integration with Bluemix. Therefore, the following modules have been deleted from the Flink Github repository:
+- `flink-contrib/docker-flink`
+- `flink-container/docker`
+- `flink-container/kubernetes`
+
+Check the updated user documentation for [Flink Docker integration](https://ci.apache.org/projects/flink/flink-docs-master/ops/deployment/docker.html) instead. It now describes in detail how to [use](https://ci.apache.org/projects/flink/flink-docs-master/ops/deployment/docker.html#how-to-run-a-flink-image) and [customize](https://ci.apache.org/projects/flink/flink-docs-master/ops/deployment/docker.html#customize-flink-image) [the Flink official docker image](https://ci.apache.org/projects/flink/flink-docs-master/ops/deployment/docker.html#docker-hub-flink-images): configuration options, logging, plugins, adding more dependencies and installing software. The documentation also includes examples for Session and Job cluster deployments with:
+- [docker run](https://ci.apache.org/projects/flink/flink-docs-master/ops/deployment/docker.html#how-to-run-flink-image)
+- [docker compose](https://ci.apache.org/projects/flink/flink-docs-master/ops/deployment/docker.html#flink-with-docker-compose)
+- [docker swarm](https://ci.apache.org/projects/flink/flink-docs-master/ops/deployment/docker.html#flink-with-docker-swarm)
+- [standalone Kubernetes](https://ci.apache.org/projects/flink/flink-docs-master/ops/deployment/kubernetes.html)
+
+### Memory Management
+#### New JobManager Memory Model
+##### Overview
+With [FLIP-116](https://cwiki.apache.org/confluence/display/FLINK/FLIP-116%3A+Unified+Memory+Configuration+for+Job+Managers), a new memory model has been introduced for the JobManager. New configuration options have been introduced to control the memory consumption of the JobManager process. This affects all types of deployments: standalone, YARN, Mesos, and the new active Kubernetes integration.
+
+Please, check the user documentation for [more details](https://ci.apache.org/projects/flink/flink-docs-master/ops/memory/mem_setup_jobmanager.html).
+
+If you try to reuse your previous Flink configuration without any adjustments, the new memory model can result in differently computed memory parameters for the JVM and, thus, performance changes or even failures.
+In order to start the JobManager process, you have to specify at least one of the following options [`jobmanager.memory.flink.size`](https://ci.apache.org/projects/flink/flink-docs-master/ops/config.html#jobmanager-memory-flink-size), [`jobmanager.memory.process.size`](https://ci.apache.org/projects/flink/flink-docs-master/ops/config.html#jobmanager-memory-process-size) or [`jobmanager.memory.heap.size`](https://ci.apache.org/projects/flink/flink-docs-master/ops/config.html#jobmanager-memory-heap-size).
+See also [the migration guide](https://ci.apache.org/projects/flink/flink-docs-master/ops/memory/mem_migration.html#migrate-job-manager-memory-configuration) for more information.
+
+##### Deprecation and breaking changes
+The following options are deprecated:
+ * `jobmanager.heap.size`
+ * `jobmanager.heap.mb`
+
+If these deprecated options are still used, they will be interpreted as one of the following new options in order to maintain backwards compatibility:
+ * [JVM Heap](https://ci.apache.org/projects/flink/flink-docs-master/ops/memory/mem_setup_jobmanager.html#configure-jvm-heap) ([`jobmanager.memory.heap.size`](https://ci.apache.org/projects/flink/flink-docs-master/ops/config.html#jobmanager-memory-heap-size)) for standalone and Mesos deployments
+ * [Total Process Memory](https://ci.apache.org/projects/flink/flink-docs-master/ops/memory/mem_setup_jobmanager.html#configure-total-memory) ([`jobmanager.memory.process.size`](https://ci.apache.org/projects/flink/flink-docs-master/ops/config.html#jobmanager-memory-process-size)) for containerized deployments (Kubernetes and Yarn)
+
+The following options have been removed and have no effect anymore:
+ * `containerized.heap-cutoff-ratio`
+ * `containerized.heap-cutoff-min`
+
+There is [no container cut-off](https://ci.apache.org/projects/flink/flink-docs-master/ops/memory/mem_migration.html#container-cut-off-memory) anymore.
+
+##### JVM arguments
+The `direct` and `metaspace` memory of the JobManager's JVM process are now limited by configurable values:
+ * [`jobmanager.memory.off-heap.size`](https://ci.apache.org/projects/flink/flink-docs-master/ops/config.html#jobmanager-memory-off-heap-size)
+ * [`jobmanager.memory.jvm-metaspace.size`](https://ci.apache.org/projects/flink/flink-docs-master/ops/config.html#jobmanager-memory-jvm-metaspace-size)
+
+See also [JVM Parameters](https://ci.apache.org/projects/flink/flink-docs-master/ops/memory/mem_setup.html#jvm-parameters).
+
+Attention These new limits can produce the respective `OutOfMemoryError` exceptions if they are not configured properly or there is a respective memory leak. See also [the troubleshooting guide](https://ci.apache.org/projects/flink/flink-docs-master/ops/memory/mem_trouble.html#outofmemoryerror-direct-buffer-memory).
+
+#### Removal of deprecated mesos.resourcemanager.tasks.mem ([FLINK-15198](https://issues.apache.org/jira/browse/FLINK-15198))
+The `mesos.resourcemanager.tasks.mem` option, deprecated in 1.10 in favour of `taskmanager.memory.process.size`, has been completely removed and will have no effect anymore in 1.11+.
+
+### Table API & SQL
+#### Blink is now the default planner ([FLINK-16934](https://issues.apache.org/jira/browse/FLINK-16934))
+The default table planner has been changed to blink.
+
+#### Changed package structure for Table API ([FLINK-15947](https://issues.apache.org/jira/browse/FLINK-15947))
+Due to various issues with packages `org.apache.flink.table.api.scala/java` all classes from those packages were relocated.
+Moreover the scala expressions were moved to `org.apache.flink.table.api` as announced in Flink 1.9.
+
+If you used one of:
+* `org.apache.flink.table.api.java.StreamTableEnvironment`
+* `org.apache.flink.table.api.scala.StreamTableEnvironment`
+* `org.apache.flink.table.api.java.BatchTableEnvironment`
+* `org.apache.flink.table.api.scala.BatchTableEnvironment`
+
+And you do not convert to/from DataStream, switch to:
+* `org.apache.flink.table.api.TableEnvironment`
+
+If you do convert to/from DataStream/DataSet, change your imports to one of:
+* `org.apache.flink.table.api.bridge.java.StreamTableEnvironment`
+* `org.apache.flink.table.api.bridge.scala.StreamTableEnvironment`
+* `org.apache.flink.table.api.bridge.java.BatchTableEnvironment`
+* `org.apache.flink.table.api.bridge.scala.BatchTableEnvironment`
+
+For the Scala expressions use the import:
+* `org.apache.flink.table.api._` instead of `org.apache.flink.table.api.bridge.scala._`
+
+Additionally, if you use Scala's implicit conversions to/from DataStream/DataSet, import `org.apache.flink.table.api.bridge.scala._` instead of `org.apache.flink.table.api.scala._`
+
+#### Removal of deprecated `StreamTableSink` ([FLINK-16362](https://issues.apache.org/jira/browse/FLINK-16362))
+The existing `StreamTableSink` implementations should remove `emitDataStream` method.
+
+#### Removal of `BatchTableSink#emitDataSet` ([FLINK-16535](https://issues.apache.org/jira/browse/FLINK-16535))
+The existing `BatchTableSink` implementations should rename `emitDataSet` to `consumeDataSet` and return `DataSink`.
+
+#### Corrected execution behavior of TableEnvironment.execute() and StreamTableEnvironment.execute() ([FLINK-16363](https://issues.apache.org/jira/browse/FLINK-16363))
+In previous versions, `TableEnvironment.execute()` and `StreamExecutionEnvironment.execute()` can both trigger table and DataStream programs.
+Since Flink 1.11.0, table programs can only be triggered by `TableEnvironment.execute()`.
+Once table program is converted into DataStream program (through `toAppendStream()` or `toRetractStream()` method), it can only be triggered by `StreamExecutionEnvironment.execute()`.
+
+#### Corrected execution behavior of ExecutionEnvironment.execute() and BatchTableEnvironment.execute() ([FLINK-17126](https://issues.apache.org/jira/browse/FLINK-17126))
+In previous versions, `BatchTableEnvironment.execute()` and `ExecutionEnvironment.execute()` can both trigger table and DataSet programs for legacy batch planner.
+Since Flink 1.11.0, batch table programs can only be triggered by `BatchEnvironment.execute()`.
+Once table program is converted into DataSet program (through `toDataSet()` method), it can only be triggered by `ExecutionEnvironment.execute()`.
+
+#### Added a changeflag to Row type ([FLINK-16998](https://issues.apache.org/jira/browse/FLINK-16998))
+An additional change flag called `RowKind` was added to the `Row` type.
+This changed the serialization format and will trigger a state migration.
+
+### Configuration
+#### Renamed log4j-yarn-session.properties and logback-yarn.xml properties files ([FLINK-17527](https://issues.apache.org/jira/browse/FLINK-17527))
+The logging properties files `log4j-yarn-session.properties` and `logback-yarn.xml` have been renamed to `log4j-session.properties` and `logback-session.xml`.
+Moreover, `yarn-session.sh` and `kubernetes-session.sh` use these logging properties files.
+
+### State
+#### Removal of deprecated background cleanup toggle (State TTL) ([FLINK-15620](https://issues.apache.org/jira/browse/FLINK-15620))
+The `StateTtlConfig#cleanupInBackground` has been removed, because the method was deprecated and the background TTL was enabled by default in 1.10.
+
+#### Removal of deprecated option to disable TTL compaction filter ([FLINK-15621](https://issues.apache.org/jira/browse/FLINK-15621))
+The TTL compaction filter in RocksDB has been enabled in 1.10 by default and it is now always enabled in 1.11+.
+Because of that the following option and methods have been removed in 1.11:
+- `state.backend.rocksdb.ttl.compaction.filter.enabled`
+- `StateTtlConfig#cleanupInRocksdbCompactFilter()`
+- `RocksDBStateBackend#isTtlCompactionFilterEnabled`
+- `RocksDBStateBackend#enableTtlCompactionFilter`
+- `RocksDBStateBackend#disableTtlCompactionFilter`
+- (state_backend.py) `is_ttl_compaction_filter_enabled`
+- (state_backend.py) `enable_ttl_compaction_filter`
+- (state_backend.py) `disable_ttl_compaction_filter`
+
+#### Changed argument type of StateBackendFactory#createFromConfig ([FLINK-16913](https://issues.apache.org/jira/browse/FLINK-16913))
+Starting from Flink 1.11 the `StateBackendFactory#createFromConfig` interface now takes `ReadableConfig` instead of `Configuration`.
+A `Configuration` class is still a valid argument to that method, as it implements the ReadableConfig interface.
+Implementors of custom `StateBackend` should adjust their implementations.
+
+#### Removal of deprecated OptionsFactory and ConfigurableOptionsFactory classes ([FLINK-18242](https://issues.apache.org/jira/browse/FLINK-18242))
+The deprecated `OptionsFactory` and `ConfigurableOptionsFactory` classes have been removed.
+Please use `RocksDBOptionsFactory` and `ConfigurableRocksDBOptionsFactory` instead.
+Please also recompile your application codes if any class extends `DefaultConfigurableOptionsFactory`.
+
+#### Enabled by default setTotalOrderSeek ([FLINK-17800](https://issues.apache.org/jira/browse/FLINK-17800))
+Since Flink-1.11 the option `setTotalOrderSeek` will be enabled by default for RocksDB's `ReadOptions`.
+This is in order to prevent user from miss using `optimizeForPointLookup`.
+For backward compatibility we support customizing `ReadOptions` through `RocksDBOptionsFactory`.
+Please set `setTotalOrderSeek` back to false if any performance regression observed (it shouldn't happen according to our testing).
+
+#### Increased default size of `state.backend.fs.memory-threshold` ([FLINK-17865](https://issues.apache.org/jira/browse/FLINK-17865))
+The default value of `state.backend.fs.memory-threshold` has been increased from 1K to 20K to prevent too many small files created on remote FS for small states.
+Jobs with large parallelism on source or stateful operators may have "JM OOM" or "RPC message exceeding maximum frame size" problem with this change.
+If you encounter such issues please manually set the configuration back to 1K.
+
+### PyFlink
+#### Throw exceptions for the unsupported data types ([FLINK-16606](https://issues.apache.org/jira/browse/FLINK-16606))
+DataTypes can be configured with some parameters, e.g., precision.
+However in previous releases, the precision provided by users was not taking any effect and default value for the precision was being used.
+To avoid confusion since Flink 1.11 exceptions will be thrown if the value is not supported to make it more visible to users.
+Changes include:
+- the precision for `TimeType` can only be `0`
+- the length for `VarBinaryType`/`VarCharType` can only be `0x7fffffff`
+- the precision/scale for `DecimalType` can only be `38`/`18`
+- the precision for `TimestampType`/`LocalZonedTimestampType` can only be `3`
+- the resolution for `DayTimeIntervalType` can only be `SECOND` and the `fractionalPrecision` can only be `3`
+- the resolution for `YearMonthIntervalType` can only be `MONTH` and the `yearPrecision` can only be `2`
+- the `CharType`/`BinaryType`/`ZonedTimestampType` is not supported
+
+### Monitoring
+#### Converted all MetricReporters to plugins ([FLINK-16963](https://issues.apache.org/jira/browse/FLINK-16963))
+All MetricReporters that come with Flink have been converted to plugins.
+They should no longer be placed into `/lib` directory (doing so may result in dependency conflicts!), but `/plugins/` instead.
+
+#### Changed of DataDog's metric reporter Counter metrics ([FLINK-15438](https://issues.apache.org/jira/browse/FLINK-15438))
+The DataDog metrics reporter now reports counts as the number of events over the reporting interval, instead of the total count.
+This aligns the count semantics with the DataDog documentation.
+
+#### Switch to Log4j 2 by default ([FLINK-15672](https://issues.apache.org/jira/browse/FLINK-15672))
+Flink now uses Log4j2 by default.
+Users who wish to revert back to Log4j1 can find instructions to do so in the logging documentation.
+
+#### Changed behaviour of JobManager API's log request ([FLINK-16303](https://issues.apache.org/jira/browse/FLINK-16303))
+Requesting an unavailable log or stdout file from the JobManager's HTTP server returns status code 404 now.
+In previous releases, the HTTP server would return a file with `(file unavailable)` as its content.
+
+#### Removal of lastCheckpointAlignmentBuffered metric ([FLINK-16404](https://issues.apache.org/jira/browse/FLINK-16404))
+Note that the metric `lastCheckpointAlignmentBuffered` has been removed, because the upstream task will not send any data after emitting a checkpoint barrier until the alignment has been completed on the downstream side.
+The web UI still displays this value but it is always `0` now.
+
+### Connectors
+#### Dropped Kafka 0.8/0.9 connectors ([FLINK-15115](https://issues.apache.org/jira/browse/FLINK-15115))
+The Kafka 0.8 and 0.9 connectors are no longer under active development and were removed.
+
+#### Dropped Elasticsearch 2.x connector ([FLINK-16046](https://issues.apache.org/jira/browse/FLINK-16046))
+The Elasticsearch 2 connector is no longer under active development and was removed.
+Prior version of these connectors will continue to work with Flink.
+
+#### Removal of deprecated `KafkaPartitioner` ([FLINK-15862](https://issues.apache.org/jira/browse/FLINK-15862))
+Deprecated `KafkaPartitioner` was removed. Please see the release notes of Flink 1.3.0 how to migrate from that interface.
+
+#### Refined fallback filesystems to only handle specific filesystems ([FLINK-16015](https://issues.apache.org/jira/browse/FLINK-16015))
+By default, if there is an official filesystem plugin for a given schema, it will not be allowed to use fallback filesystem factories (like HADOOP libraries on the classpath) to load it.
+Added `fs.allowed-fallback-filesystems` configuration option to override this behaviour.
+
+#### Deprecation of FileSystem#getKind ([FLINK-16400](https://issues.apache.org/jira/browse/FLINK-16400))
+`org.apache.flink.core.fs.FileSystem#getKind` method has been formally deprecated, as it was not used by Flink.
+
+### Runtime
+#### Streaming jobs will always fail immediately on failures in synchronous part of a checkpoint ([FLINK-17350](https://issues.apache.org/jira/browse/FLINK-17350))
+Failures in synchronous part of checkpointing (like an exceptions thrown by an operator) will fail its Task (and job) immediately, regardless of the configuration parameters.
+Since Flink 1.5 such failures could be ignored by setting `setTolerableCheckpointFailureNumber(...)` or its deprecated `setFailTaskOnCheckpointError(...)` predecessor.
+Now both options will only affect asynchronous failures.
+
+#### Checkpoint timeouts are no longer ignored by CheckpointConfig#setTolerableCheckpointFailureNumber ([FLINK-17351](https://issues.apache.org/jira/browse/FLINK-17351))
+Checkpoint timeouts will now be treated as normal checkpoint failures and checked against value configured by `CheckpointConfig#setTolerableCheckpointFailureNumber(...)`.
+
+### Miscellaneous Interface Changes
+#### Removal of deprecated StreamTask#getCheckpointLock() ([FLINK-12484](https://issues.apache.org/jira/browse/FLINK-12484))
+DataStream API no longer provides `StreamTask#getCheckpointLock` method, which was deprecated in Flink 1.10.
+Users should use `MailboxExecutor` to run actions that require synchronization with the task's thread (e.g. collecting output produced by an external thread).
+`MailboxExecutor#yield` or `MailboxExecutor#tryYield` methods can be used for actions that should give control to other actions temporarily (equivalent of `StreamTask#getCheckpointLock().wait()`), if the current operator is blocked.
+`MailboxExecutor` can be accessed by using `YieldingOperatorFactory`. Example usage can be found in the `AsyncWaitOperator`.
+
+Note, `SourceFunction.SourceContext.getCheckpointLock` is still available for custom implementations of `SourceFunction` interface.
+
+#### Reversed dependency from flink-streaming-java to flink-client ([FLINK-15090](https://issues.apache.org/jira/browse/FLINK-15090))
+Starting from Flink 1.11.0, the `flink-streaming-java` module does not have a dependency on `flink-clients` anymore. If your project was depending on this transitive dependency you now have to add `flink-clients` as an explicit dependency.
+
+#### AsyncWaitOperator is chainable again ([FLINK-16219](https://issues.apache.org/jira/browse/FLINK-16219))
+`AsyncWaitOperator` will be allowed to be chained by default with all operators, except of tasks with `SourceFunction`.
+This mostly revert limitation introduced as a bug fix for [FLINK-13063](https://issues.apache.org/jira/browse/FLINK-13063).
+
+#### Changed argument types of ShuffleEnvironment#createInputGates and #createResultPartitionWriters methods ([FLINK-16586](https://issues.apache.org/jira/browse/FLINK-16586))
+The argument type of methods `ShuffleEnvironment#createInputGates` and `#createResultPartitionWriters` are adjusted from `Collection` to `List` for satisfying the order guarantee requirement in unaligned checkpoint.
+It will break the compatibility if users already implemented a custom `ShuffleService` based on `ShuffleServiceFactory` interface.
+
+#### Deprecation of CompositeTypeSerializerSnapshot#isOuterSnapshotCompatible ([FLINK-17520](https://issues.apache.org/jira/browse/FLINK-17520))
+The `boolean isOuterSnapshotCompatible(TypeSerializer)` on the `CompositeTypeSerializerSnapshot` class has been deprecated, in favor of a new `OuterSchemaCompatibility resolveOuterSchemaCompatibility(TypeSerializer)` method.
+Please implement that instead.
+Compared to the old method, the new method allows composite serializers to signal state schema migration based on outer schema and configuration.
+
+#### Removal of deprecated TimestampExtractor ([FLINK-17655](https://issues.apache.org/jira/browse/FLINK-17655))
+The long-deprecated `TimestampExtractor` was removed along with API methods in the DataStream API.
+Please use the new `TimestampAssigner` and `WatermarkStrategies` for working with timestamps and watermarks in the DataStream API.
+
+#### Deprecation of ListCheckpointed interface ([FLINK-6258](https://issues.apache.org/jira/browse/FLINK-6258))
+The `ListCheckpointed` interface has been deprecated because it uses Java Serialization for checkpointing state which is problematic for savepoint compatibility.
+Use the `CheckpointedFunction` interface instead, which gives more control over state serialization.
+
+#### Removal of deprecated state access methods ([FLINK-17376](https://issues.apache.org/jira/browse/FLINK-17376))
+We removed deprecated state access methods `RuntimeContext#getFoldingState()`, `OperatorStateStore#getSerializableListState()` and `OperatorStateStore#getOperatorState()`.
+This means that some code that was compiled against Flink 1.10 will not work with a Flink 1.11 cluster.
+An example of this is our Kafka connector which internally used `OperatorStateStore.getSerializableListState`.
diff --git a/docs/release-notes/flink-1.11.zh.md b/docs/release-notes/flink-1.11.zh.md
new file mode 100644
index 00000000000000..7d3e67ef8b5c8f
--- /dev/null
+++ b/docs/release-notes/flink-1.11.zh.md
@@ -0,0 +1,303 @@
+---
+title: "Release Notes - Flink 1.11"
+---
+
+
+
+These release notes discuss important aspects, such as configuration, behavior,
+or dependencies, that changed between Flink 1.10 and Flink 1.11. Please read
+these notes carefully if you are planning to upgrade your Flink version to 1.11.
+
+* This will be replaced by the TOC
+{:toc}
+
+### Clusters & Deployment
+#### Support for Application Mode ([FLIP-85](https://cwiki.apache.org/confluence/display/FLINK/FLIP-85+Flink+Application+Mode))
+The user can now submit applications and choose to execute their `main()` method on the cluster rather than the client.
+This allows for more light-weight application submission. For more details,
+see the [Application Mode documentation](https://ci.apache.org/projects/flink/flink-docs-master/ops/deployment/#application-mode).
+
+#### Web Submission behaves the same as detached mode.
+With [FLINK-16657](https://issues.apache.org/jira/browse/FLINK-16657) the web submission logic changes and it exposes
+the same behavior as submitting a job through the CLI in detached mode. This implies that, for instance, jobs based on
+the DataSet API that were using sinks like `print()`, `count()` or `collect()` will now throw an exception while
+before the output was simply never printed. See also comments on related [PR](https://github.com/apache/flink/pull/11460).
+
+#### Support for Hadoop 3.0.0 and higher ([FLINK-11086](https://issues.apache.org/jira/browse/FLINK-11086))
+Flink project does not provide any updated "flink-shaded-hadoop-*" jars.
+Users need to provide Hadoop dependencies through the HADOOP_CLASSPATH environment variable (recommended) or via `lib/` folder.
+Also, the `include-hadoop` Maven profile has been removed.
+
+#### `flink-csv` and `flink-json` are bundled in lib folder ([FLINK-18173](https://issues.apache.org/jira/browse/FLINK-18173))
+There is no need to download manually jar files for `flink-csv` and `flink-json` formats as they are now bundled in the `lib` folder.
+
+#### Removal of `LegacyScheduler` ([FLINK-15629](https://issues.apache.org/jira/browse/FLINK-15629))
+Flink no longer supports the legacy scheduler.
+Hence, setting `jobmanager.scheduler: legacy` will no longer work and fail with an `IllegalArgumentException`.
+The only valid option for `jobmanager.scheduler` is the default value `ng`.
+
+#### Bind user code class loader to lifetime of a slot ([FLINK-16408](https://issues.apache.org/jira/browse/FLINK-16408))
+The user code class loader is being reused by the `TaskExecutor` as long as there is at least a single slot allocated for the respective job.
+This changes Flink's recovery behaviour slightly so that it will not reload static fields.
+The benefit is that this change drastically reduces pressure on the JVM's metaspace.
+
+#### Replaced `slave` file name with `workers` ([FLINK-18307](https://issues.apache.org/jira/browse/FLINK-18307))
+For Standalone Setups, the file with the worker nodes is no longer called `slaves` but `workers`.
+Previous setups that use the `start-cluster.sh` and `stop-cluster.sh` scripts need to rename that file.
+
+#### Flink Docker Integration Improvements
+The examples of `Dockerfiles` and docker image `build.sh` scripts have been removed from [the Flink Github repository](https://github.com/apache/flink). The examples will no longer be maintained by community in the Flink Github repository, including the examples of integration with Bluemix. Therefore, the following modules have been deleted from the Flink Github repository:
+- `flink-contrib/docker-flink`
+- `flink-container/docker`
+- `flink-container/kubernetes`
+
+Check the updated user documentation for [Flink Docker integration](https://ci.apache.org/projects/flink/flink-docs-master/ops/deployment/docker.html) instead. It now describes in detail how to [use](https://ci.apache.org/projects/flink/flink-docs-master/ops/deployment/docker.html#how-to-run-a-flink-image) and [customize](https://ci.apache.org/projects/flink/flink-docs-master/ops/deployment/docker.html#customize-flink-image) [the Flink official docker image](https://ci.apache.org/projects/flink/flink-docs-master/ops/deployment/docker.html#docker-hub-flink-images): configuration options, logging, plugins, adding more dependencies and installing software. The documentation also includes examples for Session and Job cluster deployments with:
+- [docker run](https://ci.apache.org/projects/flink/flink-docs-master/ops/deployment/docker.html#how-to-run-flink-image)
+- [docker compose](https://ci.apache.org/projects/flink/flink-docs-master/ops/deployment/docker.html#flink-with-docker-compose)
+- [docker swarm](https://ci.apache.org/projects/flink/flink-docs-master/ops/deployment/docker.html#flink-with-docker-swarm)
+- [standalone Kubernetes](https://ci.apache.org/projects/flink/flink-docs-master/ops/deployment/kubernetes.html)
+
+### Memory Management
+#### New JobManager Memory Model
+##### Overview
+With [FLIP-116](https://cwiki.apache.org/confluence/display/FLINK/FLIP-116%3A+Unified+Memory+Configuration+for+Job+Managers), a new memory model has been introduced for the JobManager. New configuration options have been introduced to control the memory consumption of the JobManager process. This affects all types of deployments: standalone, YARN, Mesos, and the new active Kubernetes integration.
+
+Please, check the user documentation for [more details](https://ci.apache.org/projects/flink/flink-docs-master/ops/memory/mem_setup_jobmanager.html).
+
+If you try to reuse your previous Flink configuration without any adjustments, the new memory model can result in differently computed memory parameters for the JVM and, thus, performance changes or even failures.
+In order to start the JobManager process, you have to specify at least one of the following options [`jobmanager.memory.flink.size`](https://ci.apache.org/projects/flink/flink-docs-master/ops/config.html#jobmanager-memory-flink-size), [`jobmanager.memory.process.size`](https://ci.apache.org/projects/flink/flink-docs-master/ops/config.html#jobmanager-memory-process-size) or [`jobmanager.memory.heap.size`](https://ci.apache.org/projects/flink/flink-docs-master/ops/config.html#jobmanager-memory-heap-size).
+See also [the migration guide](https://ci.apache.org/projects/flink/flink-docs-master/ops/memory/mem_migration.html#migrate-job-manager-memory-configuration) for more information.
+
+##### Deprecation and breaking changes
+The following options are deprecated:
+ * `jobmanager.heap.size`
+ * `jobmanager.heap.mb`
+
+If these deprecated options are still used, they will be interpreted as one of the following new options in order to maintain backwards compatibility:
+ * [JVM Heap](https://ci.apache.org/projects/flink/flink-docs-master/ops/memory/mem_setup_jobmanager.html#configure-jvm-heap) ([`jobmanager.memory.heap.size`](https://ci.apache.org/projects/flink/flink-docs-master/ops/config.html#jobmanager-memory-heap-size)) for standalone and Mesos deployments
+ * [Total Process Memory](https://ci.apache.org/projects/flink/flink-docs-master/ops/memory/mem_setup_jobmanager.html#configure-total-memory) ([`jobmanager.memory.process.size`](https://ci.apache.org/projects/flink/flink-docs-master/ops/config.html#jobmanager-memory-process-size)) for containerized deployments (Kubernetes and Yarn)
+
+The following options have been removed and have no effect anymore:
+ * `containerized.heap-cutoff-ratio`
+ * `containerized.heap-cutoff-min`
+
+There is [no container cut-off](https://ci.apache.org/projects/flink/flink-docs-master/ops/memory/mem_migration.html#container-cut-off-memory) anymore.
+
+##### JVM arguments
+The `direct` and `metaspace` memory of the JobManager's JVM process are now limited by configurable values:
+ * [`jobmanager.memory.off-heap.size`](https://ci.apache.org/projects/flink/flink-docs-master/ops/config.html#jobmanager-memory-off-heap-size)
+ * [`jobmanager.memory.jvm-metaspace.size`](https://ci.apache.org/projects/flink/flink-docs-master/ops/config.html#jobmanager-memory-jvm-metaspace-size)
+
+See also [JVM Parameters](https://ci.apache.org/projects/flink/flink-docs-master/ops/memory/mem_setup.html#jvm-parameters).
+
+Attention These new limits can produce the respective `OutOfMemoryError` exceptions if they are not configured properly or there is a respective memory leak. See also [the troubleshooting guide](https://ci.apache.org/projects/flink/flink-docs-master/ops/memory/mem_trouble.html#outofmemoryerror-direct-buffer-memory).
+
+#### Removal of deprecated mesos.resourcemanager.tasks.mem ([FLINK-15198](https://issues.apache.org/jira/browse/FLINK-15198))
+The `mesos.resourcemanager.tasks.mem` option, deprecated in 1.10 in favour of `taskmanager.memory.process.size`, has been completely removed and will have no effect anymore in 1.11+.
+
+### Table API & SQL
+#### Blink is now the default planner ([FLINK-16934](https://issues.apache.org/jira/browse/FLINK-16934))
+The default table planner has been changed to blink.
+
+#### Changed package structure for Table API ([FLINK-15947](https://issues.apache.org/jira/browse/FLINK-15947))
+Due to various issues with packages `org.apache.flink.table.api.scala/java` all classes from those packages were relocated.
+Moreover the scala expressions were moved to `org.apache.flink.table.api` as announced in Flink 1.9.
+
+If you used one of:
+* `org.apache.flink.table.api.java.StreamTableEnvironment`
+* `org.apache.flink.table.api.scala.StreamTableEnvironment`
+* `org.apache.flink.table.api.java.BatchTableEnvironment`
+* `org.apache.flink.table.api.scala.BatchTableEnvironment`
+
+And you do not convert to/from DataStream, switch to:
+* `org.apache.flink.table.api.TableEnvironment`
+
+If you do convert to/from DataStream/DataSet, change your imports to one of:
+* `org.apache.flink.table.api.bridge.java.StreamTableEnvironment`
+* `org.apache.flink.table.api.bridge.scala.StreamTableEnvironment`
+* `org.apache.flink.table.api.bridge.java.BatchTableEnvironment`
+* `org.apache.flink.table.api.bridge.scala.BatchTableEnvironment`
+
+For the Scala expressions use the import:
+* `org.apache.flink.table.api._` instead of `org.apache.flink.table.api.bridge.scala._`
+
+Additionally, if you use Scala's implicit conversions to/from DataStream/DataSet, import `org.apache.flink.table.api.bridge.scala._` instead of `org.apache.flink.table.api.scala._`
+
+#### Removal of deprecated `StreamTableSink` ([FLINK-16362](https://issues.apache.org/jira/browse/FLINK-16362))
+The existing `StreamTableSink` implementations should remove `emitDataStream` method.
+
+#### Removal of `BatchTableSink#emitDataSet` ([FLINK-16535](https://issues.apache.org/jira/browse/FLINK-16535))
+The existing `BatchTableSink` implementations should rename `emitDataSet` to `consumeDataSet` and return `DataSink`.
+
+#### Corrected execution behavior of TableEnvironment.execute() and StreamTableEnvironment.execute() ([FLINK-16363](https://issues.apache.org/jira/browse/FLINK-16363))
+In previous versions, `TableEnvironment.execute()` and `StreamExecutionEnvironment.execute()` can both trigger table and DataStream programs.
+Since Flink 1.11.0, table programs can only be triggered by `TableEnvironment.execute()`.
+Once table program is converted into DataStream program (through `toAppendStream()` or `toRetractStream()` method), it can only be triggered by `StreamExecutionEnvironment.execute()`.
+
+#### Corrected execution behavior of ExecutionEnvironment.execute() and BatchTableEnvironment.execute() ([FLINK-17126](https://issues.apache.org/jira/browse/FLINK-17126))
+In previous versions, `BatchTableEnvironment.execute()` and `ExecutionEnvironment.execute()` can both trigger table and DataSet programs for legacy batch planner.
+Since Flink 1.11.0, batch table programs can only be triggered by `BatchEnvironment.execute()`.
+Once table program is converted into DataSet program (through `toDataSet()` method), it can only be triggered by `ExecutionEnvironment.execute()`.
+
+#### Added a changeflag to Row type ([FLINK-16998](https://issues.apache.org/jira/browse/FLINK-16998))
+An additional change flag called `RowKind` was added to the `Row` type.
+This changed the serialization format and will trigger a state migration.
+
+### Configuration
+#### Renamed log4j-yarn-session.properties and logback-yarn.xml properties files ([FLINK-17527](https://issues.apache.org/jira/browse/FLINK-17527))
+The logging properties files `log4j-yarn-session.properties` and `logback-yarn.xml` have been renamed to `log4j-session.properties` and `logback-session.xml`.
+Moreover, `yarn-session.sh` and `kubernetes-session.sh` use these logging properties files.
+
+### State
+#### Removal of deprecated background cleanup toggle (State TTL) ([FLINK-15620](https://issues.apache.org/jira/browse/FLINK-15620))
+The `StateTtlConfig#cleanupInBackground` has been removed, because the method was deprecated and the background TTL was enabled by default in 1.10.
+
+#### Removal of deprecated option to disable TTL compaction filter ([FLINK-15621](https://issues.apache.org/jira/browse/FLINK-15621))
+The TTL compaction filter in RocksDB has been enabled in 1.10 by default and it is now always enabled in 1.11+.
+Because of that the following option and methods have been removed in 1.11:
+- `state.backend.rocksdb.ttl.compaction.filter.enabled`
+- `StateTtlConfig#cleanupInRocksdbCompactFilter()`
+- `RocksDBStateBackend#isTtlCompactionFilterEnabled`
+- `RocksDBStateBackend#enableTtlCompactionFilter`
+- `RocksDBStateBackend#disableTtlCompactionFilter`
+- (state_backend.py) `is_ttl_compaction_filter_enabled`
+- (state_backend.py) `enable_ttl_compaction_filter`
+- (state_backend.py) `disable_ttl_compaction_filter`
+
+#### Changed argument type of StateBackendFactory#createFromConfig ([FLINK-16913](https://issues.apache.org/jira/browse/FLINK-16913))
+Starting from Flink 1.11 the `StateBackendFactory#createFromConfig` interface now takes `ReadableConfig` instead of `Configuration`.
+A `Configuration` class is still a valid argument to that method, as it implements the ReadableConfig interface.
+Implementors of custom `StateBackend` should adjust their implementations.
+
+#### Removal of deprecated OptionsFactory and ConfigurableOptionsFactory classes ([FLINK-18242](https://issues.apache.org/jira/browse/FLINK-18242))
+The deprecated `OptionsFactory` and `ConfigurableOptionsFactory` classes have been removed.
+Please use `RocksDBOptionsFactory` and `ConfigurableRocksDBOptionsFactory` instead.
+Please also recompile your application codes if any class extends `DefaultConfigurableOptionsFactory`.
+
+#### Enabled by default setTotalOrderSeek ([FLINK-17800](https://issues.apache.org/jira/browse/FLINK-17800))
+Since Flink-1.11 the option `setTotalOrderSeek` will be enabled by default for RocksDB's `ReadOptions`.
+This is in order to prevent user from miss using `optimizeForPointLookup`.
+For backward compatibility we support customizing `ReadOptions` through `RocksDBOptionsFactory`.
+Please set `setTotalOrderSeek` back to false if any performance regression observed (it shouldn't happen according to our testing).
+
+#### Increased default size of `state.backend.fs.memory-threshold` ([FLINK-17865](https://issues.apache.org/jira/browse/FLINK-17865))
+The default value of `state.backend.fs.memory-threshold` has been increased from 1K to 20K to prevent too many small files created on remote FS for small states.
+Jobs with large parallelism on source or stateful operators may have "JM OOM" or "RPC message exceeding maximum frame size" problem with this change.
+If you encounter such issues please manually set the configuration back to 1K.
+
+### PyFlink
+#### Throw exceptions for the unsupported data types ([FLINK-16606](https://issues.apache.org/jira/browse/FLINK-16606))
+DataTypes can be configured with some parameters, e.g., precision.
+However in previous releases, the precision provided by users was not taking any effect and default value for the precision was being used.
+To avoid confusion since Flink 1.11 exceptions will be thrown if the value is not supported to make it more visible to users.
+Changes include:
+- the precision for `TimeType` can only be `0`
+- the length for `VarBinaryType`/`VarCharType` can only be `0x7fffffff`
+- the precision/scale for `DecimalType` can only be `38`/`18`
+- the precision for `TimestampType`/`LocalZonedTimestampType` can only be `3`
+- the resolution for `DayTimeIntervalType` can only be `SECOND` and the `fractionalPrecision` can only be `3`
+- the resolution for `YearMonthIntervalType` can only be `MONTH` and the `yearPrecision` can only be `2`
+- the `CharType`/`BinaryType`/`ZonedTimestampType` is not supported
+
+### Monitoring
+#### Converted all MetricReporters to plugins ([FLINK-16963](https://issues.apache.org/jira/browse/FLINK-16963))
+All MetricReporters that come with Flink have been converted to plugins.
+They should no longer be placed into `/lib` directory (doing so may result in dependency conflicts!), but `/plugins/` instead.
+
+#### Changed of DataDog's metric reporter Counter metrics ([FLINK-15438](https://issues.apache.org/jira/browse/FLINK-15438))
+The DataDog metrics reporter now reports counts as the number of events over the reporting interval, instead of the total count.
+This aligns the count semantics with the DataDog documentation.
+
+#### Switch to Log4j 2 by default ([FLINK-15672](https://issues.apache.org/jira/browse/FLINK-15672))
+Flink now uses Log4j2 by default.
+Users who wish to revert back to Log4j1 can find instructions to do so in the logging documentation.
+
+#### Changed behaviour of JobManager API's log request ([FLINK-16303](https://issues.apache.org/jira/browse/FLINK-16303))
+Requesting an unavailable log or stdout file from the JobManager's HTTP server returns status code 404 now.
+In previous releases, the HTTP server would return a file with `(file unavailable)` as its content.
+
+#### Removal of lastCheckpointAlignmentBuffered metric ([FLINK-16404](https://issues.apache.org/jira/browse/FLINK-16404))
+Note that the metric `lastCheckpointAlignmentBuffered` has been removed, because the upstream task will not send any data after emitting a checkpoint barrier until the alignment has been completed on the downstream side.
+The web UI still displays this value but it is always `0` now.
+
+### Connectors
+#### Dropped Kafka 0.8/0.9 connectors ([FLINK-15115](https://issues.apache.org/jira/browse/FLINK-15115))
+The Kafka 0.8 and 0.9 connectors are no longer under active development and were removed.
+
+#### Dropped Elasticsearch 2.x connector ([FLINK-16046](https://issues.apache.org/jira/browse/FLINK-16046))
+The Elasticsearch 2 connector is no longer under active development and was removed.
+Prior version of these connectors will continue to work with Flink.
+
+#### Removal of deprecated `KafkaPartitioner` ([FLINK-15862](https://issues.apache.org/jira/browse/FLINK-15862))
+Deprecated `KafkaPartitioner` was removed. Please see the release notes of Flink 1.3.0 how to migrate from that interface.
+
+#### Refined fallback filesystems to only handle specific filesystems ([FLINK-16015](https://issues.apache.org/jira/browse/FLINK-16015))
+By default, if there is an official filesystem plugin for a given schema, it will not be allowed to use fallback filesystem factories (like HADOOP libraries on the classpath) to load it.
+Added `fs.allowed-fallback-filesystems` configuration option to override this behaviour.
+
+#### Deprecation of FileSystem#getKind ([FLINK-16400](https://issues.apache.org/jira/browse/FLINK-16400))
+`org.apache.flink.core.fs.FileSystem#getKind` method has been formally deprecated, as it was not used by Flink.
+
+### Runtime
+#### Streaming jobs will always fail immediately on failures in synchronous part of a checkpoint ([FLINK-17350](https://issues.apache.org/jira/browse/FLINK-17350))
+Failures in synchronous part of checkpointing (like an exceptions thrown by an operator) will fail its Task (and job) immediately, regardless of the configuration parameters.
+Since Flink 1.5 such failures could be ignored by setting `setTolerableCheckpointFailureNumber(...)` or its deprecated `setFailTaskOnCheckpointError(...)` predecessor.
+Now both options will only affect asynchronous failures.
+
+#### Checkpoint timeouts are no longer ignored by CheckpointConfig#setTolerableCheckpointFailureNumber ([FLINK-17351](https://issues.apache.org/jira/browse/FLINK-17351))
+Checkpoint timeouts will now be treated as normal checkpoint failures and checked against value configured by `CheckpointConfig#setTolerableCheckpointFailureNumber(...)`.
+
+### Miscellaneous Interface Changes
+#### Removal of deprecated StreamTask#getCheckpointLock() ([FLINK-12484](https://issues.apache.org/jira/browse/FLINK-12484))
+DataStream API no longer provides `StreamTask#getCheckpointLock` method, which was deprecated in Flink 1.10.
+Users should use `MailboxExecutor` to run actions that require synchronization with the task's thread (e.g. collecting output produced by an external thread).
+`MailboxExecutor#yield` or `MailboxExecutor#tryYield` methods can be used for actions that should give control to other actions temporarily (equivalent of `StreamTask#getCheckpointLock().wait()`), if the current operator is blocked.
+`MailboxExecutor` can be accessed by using `YieldingOperatorFactory`. Example usage can be found in the `AsyncWaitOperator`.
+
+Note, `SourceFunction.SourceContext.getCheckpointLock` is still available for custom implementations of `SourceFunction` interface.
+
+#### Reversed dependency from flink-streaming-java to flink-client ([FLINK-15090](https://issues.apache.org/jira/browse/FLINK-15090))
+Starting from Flink 1.11.0, the `flink-streaming-java` module does not have a dependency on `flink-clients` anymore. If your project was depending on this transitive dependency you now have to add `flink-clients` as an explicit dependency.
+
+#### AsyncWaitOperator is chainable again ([FLINK-16219](https://issues.apache.org/jira/browse/FLINK-16219))
+`AsyncWaitOperator` will be allowed to be chained by default with all operators, except of tasks with `SourceFunction`.
+This mostly revert limitation introduced as a bug fix for [FLINK-13063](https://issues.apache.org/jira/browse/FLINK-13063).
+
+#### Changed argument types of ShuffleEnvironment#createInputGates and #createResultPartitionWriters methods ([FLINK-16586](https://issues.apache.org/jira/browse/FLINK-16586))
+The argument type of methods `ShuffleEnvironment#createInputGates` and `#createResultPartitionWriters` are adjusted from `Collection` to `List` for satisfying the order guarantee requirement in unaligned checkpoint.
+It will break the compatibility if users already implemented a custom `ShuffleService` based on `ShuffleServiceFactory` interface.
+
+#### Deprecation of CompositeTypeSerializerSnapshot#isOuterSnapshotCompatible ([FLINK-17520](https://issues.apache.org/jira/browse/FLINK-17520))
+The `boolean isOuterSnapshotCompatible(TypeSerializer)` on the `CompositeTypeSerializerSnapshot` class has been deprecated, in favor of a new `OuterSchemaCompatibility resolveOuterSchemaCompatibility(TypeSerializer)` method.
+Please implement that instead.
+Compared to the old method, the new method allows composite serializers to signal state schema migration based on outer schema and configuration.
+
+#### Removal of deprecated TimestampExtractor ([FLINK-17655](https://issues.apache.org/jira/browse/FLINK-17655))
+The long-deprecated `TimestampExtractor` was removed along with API methods in the DataStream API.
+Please use the new `TimestampAssigner` and `WatermarkStrategies` for working with timestamps and watermarks in the DataStream API.
+
+#### Deprecation of ListCheckpointed interface ([FLINK-6258](https://issues.apache.org/jira/browse/FLINK-6258))
+The `ListCheckpointed` interface has been deprecated because it uses Java Serialization for checkpointing state which is problematic for savepoint compatibility.
+Use the `CheckpointedFunction` interface instead, which gives more control over state serialization.
+
+#### Removal of deprecated state access methods ([FLINK-17376](https://issues.apache.org/jira/browse/FLINK-17376))
+We removed deprecated state access methods `RuntimeContext#getFoldingState()`, `OperatorStateStore#getSerializableListState()` and `OperatorStateStore#getOperatorState()`.
+This means that some code that was compiled against Flink 1.10 will not work with a Flink 1.11 cluster.
+An example of this is our Kafka connector which internally used `OperatorStateStore.getSerializableListState`.
diff --git a/docs/training/datastream_api.md b/docs/training/datastream_api.md
deleted file mode 100644
index 7392041d54fb9b..00000000000000
--- a/docs/training/datastream_api.md
+++ /dev/null
@@ -1,236 +0,0 @@
----
-title: Intro to the DataStream API
-nav-id: datastream-api
-nav-pos: 2
-nav-title: Intro to the DataStream API
-nav-parent_id: training
----
-
-
-The focus of this training is to broadly cover the DataStream API well enough that you will be able
-to get started writing streaming applications.
-
-* This will be replaced by the TOC
-{:toc}
-
-## What can be Streamed?
-
-Flink's DataStream APIs for Java and Scala will let you stream anything they can serialize. Flink's
-own serializer is used for
-
-- basic types, i.e., String, Long, Integer, Boolean, Array
-- composite types: Tuples, POJOs, and Scala case classes
-
-and Flink falls back to Kryo for other types. It is also possible to use other serializers with
-Flink. Avro, in particular, is well supported.
-
-### Java tuples and POJOs
-
-Flink's native serializer can operate efficiently on tuples and POJOs.
-
-#### Tuples
-
-For Java, Flink defines its own `Tuple0` thru `Tuple25` types.
-
-{% highlight java %}
-Tuple2 person = Tuple2.of("Fred", 35);
-
-// zero based index!
-String name = person.f0;
-Integer age = person.f1;
-{% endhighlight %}
-
-#### POJOs
-
-Flink recognizes a data type as a POJO type (and allows “by-name” field referencing) if the following conditions are fulfilled:
-
-- The class is public and standalone (no non-static inner class)
-- The class has a public no-argument constructor
-- All non-static, non-transient fields in the class (and all superclasses) are either public (and
- non-final) or have public getter- and setter- methods that follow the Java beans naming
- conventions for getters and setters.
-
-Example:
-
-{% highlight java %}
-public class Person {
- public String name;
- public Integer age;
- public Person() {};
- public Person(String name, Integer age) {
- . . .
- };
-}
-
-Person person = new Person("Fred Flintstone", 35);
-{% endhighlight %}
-
-Flink's serializer [supports schema evolution for POJO types]({% link dev/stream/state/schema_evolution.md %}#pojo-types).
-
-### Scala tuples and case classes
-
-These work just as you'd expect.
-
-{% top %}
-
-## A Complete Example
-
-This example takes a stream of records about people as input, and filters it to only include the adults.
-
-{% highlight java %}
-import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
-import org.apache.flink.streaming.api.datastream.DataStream;
-import org.apache.flink.api.common.functions.FilterFunction;
-
-public class Example {
-
- public static void main(String[] args) throws Exception {
- final StreamExecutionEnvironment env =
- StreamExecutionEnvironment.getExecutionEnvironment();
-
- DataStream flintstones = env.fromElements(
- new Person("Fred", 35),
- new Person("Wilma", 35),
- new Person("Pebbles", 2));
-
- DataStream adults = flintstones.filter(new FilterFunction() {
- @Override
- public boolean filter(Person person) throws Exception {
- return person.age >= 18;
- }
- });
-
- adults.print();
-
- env.execute();
- }
-
- public static class Person {
- public String name;
- public Integer age;
- public Person() {};
-
- public Person(String name, Integer age) {
- this.name = name;
- this.age = age;
- };
-
- public String toString() {
- return this.name.toString() + ": age " + this.age.toString();
- };
- }
-}
-{% endhighlight %}
-
-### Stream execution environment
-
-Every Flink application needs an execution environment, `env` in this example. Streaming
-applications need to use a `StreamExecutionEnvironment`.
-
-The DataStream API calls made in your application build a job graph that is attached to the
-`StreamExecutionEnvironment`. When `env.execute()` is called this graph is packaged up and sent to
-the Flink Master, which parallelizes the job and distributes slices of it to the Task Managers for
-execution. Each parallel slice of your job will be executed in a *task slot*.
-
-Note that if you don't call execute(), your application won't be run.
-
-
-
-This distributed runtime depends on your application being serializable. It also requires that all
-dependencies are available to each node in the cluster.
-
-### Basic stream sources
-
-The example above constructs a `DataStream` using `env.fromElements(...)`. This is a
-convenient way to throw together a simple stream for use in a prototype or test. There is also a
-`fromCollection(Collection)` method on `StreamExecutionEnvironment`. So instead, you could do this:
-
-{% highlight java %}
-List people = new ArrayList();
-
-people.add(new Person("Fred", 35));
-people.add(new Person("Wilma", 35));
-people.add(new Person("Pebbles", 2));
-
-DataStream flintstones = env.fromCollection(people);
-{% endhighlight %}
-
-Another convenient way to get some data into a stream while prototyping is to use a socket
-
-{% highlight java %}
-DataStream lines = env.socketTextStream("localhost", 9999)
-{% endhighlight %}
-
-or a file
-
-{% highlight java %}
-DataStream lines = env.readTextFile("file:///path");
-{% endhighlight %}
-
-In real applications the most commonly used data sources are those that support low-latency, high
-throughput parallel reads in combination with rewind and replay -- the prerequisites for high
-performance and fault tolerance -- such as Apache Kafka, Kinesis, and various filesystems. REST APIs
-and databases are also frequently used for stream enrichment.
-
-### Basic stream sinks
-
-The example above uses `adults.print()` to print its results to the task manager logs (which will
-appear in your IDE's console, when running in an IDE). This will call `toString()` on each element
-of the stream.
-
-The output looks something like this
-
- 1> Fred: age 35
- 2> Wilma: age 35
-
-where 1> and 2> indicate which sub-task (i.e., thread) produced the output.
-
-In production, commonly used sinks include the StreamingFileSink, various databases,
-and several pub-sub systems.
-
-### Debugging
-
-In production, your application will run in a remote cluster or set of containers. And if it fails,
-it will fail remotely. The Flink Master and Task Manager logs can be very helpful in debugging such
-failures, but it is much easier to do local debugging inside an IDE, which is something that Flink
-supports. You can set breakpoints, examine local variables, and step through your code. You can also
-step into Flink's code, which can be a great way to learn more about its internals if you are
-curious to see how Flink works.
-
-{% top %}
-
-## Hands-on
-
-At this point you know enough to get started coding and running a simple DataStream application.
-Clone the [flink-training repo](https://github.com/apache/flink-training/tree/{% if site.is_stable %}release-{{ site.version_title }}{% else %}master{% endif %}), and after following the
-instructions in the README, do the first exercise:
-[Filtering a Stream (Ride Cleansing)](https://github.com/apache/flink-training/tree/{% if site.is_stable %}release-{{ site.version_title }}{% else %}master{% endif %}/ride-cleansing).
-
-{% top %}
-
-## Further Reading
-
-- [Flink Serialization Tuning Vol. 1: Choosing your Serializer — if you can](https://flink.apache.org/news/2020/04/15/flink-serialization-tuning-vol-1.html)
-- [Anatomy of a Flink Program]({% link dev/datastream_api.md %}#anatomy-of-a-flink-program)
-- [Data Sources]({% link dev/datastream_api.md %}#data-sources)
-- [Data Sinks]({% link dev/datastream_api.md %}#data-sinks)
-- [DataStream Connectors]({% link dev/connectors/index.md %})
-
-{% top %}
diff --git a/docs/training/datastream_api.zh.md b/docs/training/datastream_api.zh.md
deleted file mode 100644
index b5f76065feb8a6..00000000000000
--- a/docs/training/datastream_api.zh.md
+++ /dev/null
@@ -1,213 +0,0 @@
----
-title: DataStream API 简介
-nav-id: datastream-api
-nav-pos: 2
-nav-title: DataStream API 简介
-nav-parent_id: training
----
-
-
-该练习的重点是充分全面地了解 DataStream API,以便于编写流式应用入门。
-
-* This will be replaced by the TOC
-{:toc}
-
-## 什么能被转化成流?
-
-Flink 的 Java 和 Scala DataStream API 可以将任何可序列化的对象转化为流。Flink 自带的序列化器有
-
-- 基本类型,即 String、Long、Integer、Boolean、Array
-- 复合类型:Tuples、POJOs 和 Scala case classes
-
-而且 Flink 会交给 Kryo 序列化其他类型。也可以将其他序列化器和 Flink 一起使用。特别是有良好支持的 Avro。
-
-### Java tuples 和 POJOs
-
-Flink 的原生序列化器可以高效地操作 tuples 和 POJOs
-
-#### Tuples
-
-对于 Java,Flink 自带有 `Tuple0` 到 `Tuple25` 类型。
-
-{% highlight java %}
-Tuple2 person = Tuple2.of("Fred", 35);
-
-// zero based index!
-String name = person.f0;
-Integer age = person.f1;
-{% endhighlight %}
-
-#### POJOs
-
-如果满足以下条件,Flink 将数据类型识别为 POJO 类型(并允许“按名称”字段引用):
-
-- 该类是公有且独立的(没有非静态内部类)
-- 该类有公有的无参构造函数
-- 类(及父类)中所有的所有不被 static、transient 修饰的属性要么是公有的(且不被 final 修饰),要么是包含公有的 getter 和 setter 方法,这些方法遵循 Java bean 命名规范。
-
-示例:
-
-{% highlight java %}
-public class Person {
- public String name;
- public Integer age;
- public Person() {};
- public Person(String name, Integer age) {
- . . .
- };
-}
-
-Person person = new Person("Fred Flintstone", 35);
-{% endhighlight %}
-
-Flink 的序列化器[支持的 POJO 类型数据结构升级]({% link dev/stream/state/schema_evolution.zh.md %}#pojo-types)。
-
-### Scala tuples 和 case classes
-
-如果你了解 Scala,那一定知道 tuple 和 case class。
-
-{% top %}
-
-## 一个完整的示例
-
-该示例将关于人的记录流作为输入,并且过滤后只包含成年人。
-
-{% highlight java %}
-import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
-import org.apache.flink.streaming.api.datastream.DataStream;
-import org.apache.flink.api.common.functions.FilterFunction;
-
-public class Example {
-
- public static void main(String[] args) throws Exception {
- final StreamExecutionEnvironment env =
- StreamExecutionEnvironment.getExecutionEnvironment();
-
- DataStream flintstones = env.fromElements(
- new Person("Fred", 35),
- new Person("Wilma", 35),
- new Person("Pebbles", 2));
-
- DataStream adults = flintstones.filter(new FilterFunction() {
- @Override
- public boolean filter(Person person) throws Exception {
- return person.age >= 18;
- }
- });
-
- adults.print();
-
- env.execute();
- }
-
- public static class Person {
- public String name;
- public Integer age;
- public Person() {};
-
- public Person(String name, Integer age) {
- this.name = name;
- this.age = age;
- };
-
- public String toString() {
- return this.name.toString() + ": age " + this.age.toString();
- };
- }
-}
-{% endhighlight %}
-
-### Stream 执行环境
-
-每个 Flink 应用都需要有执行环境,在该示例中为 `env`。流式应用需要用到 `StreamExecutionEnvironment`。
-
-DataStream API 将你的应用构建为一个 job graph,并附加到 `StreamExecutionEnvironment` 。当调用 `env.execute()` 时此 graph 就被打包并发送到 Flink Master 上,后者对作业并行处理并将其子任务分发给 Task Manager 来执行。每个作业的并行子任务将在 *task slot* 中执行。
-
-注意,如果没有调用 execute(),应用就不会运行。
-
-
-
-此分布式运行时取决于你的应用是否是可序列化的。它还要求所有依赖对集群中的每个节点均可用。
-
-### 基本的 stream source
-
-上述示例用 `env.fromElements(...)` 方法构造 `DataStream` 。这样将简单的流放在一起是为了方便用于原型或测试。`StreamExecutionEnvironment` 上还有一个 `fromCollection(Collection)` 方法。因此,你可以这样做:
-
-{% highlight java %}
-List people = new ArrayList();
-
-people.add(new Person("Fred", 35));
-people.add(new Person("Wilma", 35));
-people.add(new Person("Pebbles", 2));
-
-DataStream flintstones = env.fromCollection(people);
-{% endhighlight %}
-
-另一个获取数据到流中的便捷方法是用 socket
-
-{% highlight java %}
-DataStream lines = env.socketTextStream("localhost", 9999)
-{% endhighlight %}
-
-或读取文件
-
-{% highlight java %}
-DataStream lines = env.readTextFile("file:///path");
-{% endhighlight %}
-
-在真实的应用中,最常用的数据源是那些支持低延迟,高吞吐并行读取以及重复(高性能和容错能力为先决条件)的数据源,例如 Apache Kafka,Kinesis 和各种文件系统。REST API 和数据库也经常用于增强流处理的能力(stream enrichment)。
-
-### 基本的 stream sink
-
-上述示例用 `adults.print()` 打印其结果到 task manager 的日志中(如果运行在 IDE 中时,将追加到你的 IDE 控制台)。它会对流中的每个元素都调用 `toString()` 方法。
-
-输出看起来类似于
-
- 1> Fred: age 35
- 2> Wilma: age 35
-
-1> 和 2> 指出输出来自哪个 sub-task(即 thread)
-
-In production, commonly used sinks include the StreamingFileSink, various databases,
-and several pub-sub systems.
-
-### 调试
-
-在生产中,应用程序将在远程集群或一组容器中运行。如果集群或容器挂了,这就属于远程失败。Flink Master 和 Task Manager 日志对于调试此类故障非常有用,但是更简单的是 Flink 支持在 IDE 内部进行本地调试。你可以设置断点,检查局部变量,并逐行执行代码。如果想了解 Flink 的工作原理和内部细节,查看 Flink 源码也是非常好的方法。
-
-{% top %}
-
-## 动手实践
-
-至此,你已经可以开始编写并运行一个简单的 DataStream 应用了。
-克隆 [flink-training repo](https://github.com/apache/flink-training/tree/{% if site.is_stable %}release-{{ site.version_title }}{% else %}master{% endif %}) 并在阅读完 README 中的指示后,开始尝试第一个练习吧:
-[Filtering a Stream (Ride Cleansing)](https://github.com/apache/flink-training/tree/{% if site.is_stable %}release-{{ site.version_title }}{% else %}master{% endif %}/ride-cleansing)。
-
-{% top %}
-
-## 更多阅读
-
-- [Flink Serialization Tuning Vol. 1: Choosing your Serializer — if you can](https://flink.apache.org/news/2020/04/15/flink-serialization-tuning-vol-1.html)
-- [Anatomy of a Flink Program]({% link dev/api_concepts.zh.md %}#anatomy-of-a-flink-program)
-- [Data Sources]({% link dev/datastream_api.zh.md %}#data-sources)
-- [Data Sinks]({% link dev/datastream_api.zh.md %}#data-sinks)
-- [DataStream Connectors]({% link dev/connectors/index.zh.md %})
-
-{% top %}
diff --git a/docs/training/fault_tolerance.zh.md b/docs/training/fault_tolerance.zh.md
deleted file mode 100644
index 55a26de0e60820..00000000000000
--- a/docs/training/fault_tolerance.zh.md
+++ /dev/null
@@ -1,208 +0,0 @@
----
-title: Fault Tolerance via State Snapshots
-nav-id: fault-tolerance
-nav-pos: 6
-nav-title: Fault Tolerance
-nav-parent_id: training
----
-
-
-* This will be replaced by the TOC
-{:toc}
-
-## State Backends
-
-The keyed state managed by Flink is a sort of sharded, key/value store, and the working copy of each
-item of keyed state is kept somewhere local to the taskmanager responsible for that key. Operator
-state is also local to the machine(s) that need(s) it. Flink periodically takes persistent snapshots
-of all the state and copies these snapshots somewhere more durable, such as a distributed file
-system.
-
-In the event of the failure, Flink can restore the complete state of your application and resume
-processing as though nothing had gone wrong.
-
-This state that Flink manages is stored in a _state backend_. Two implementations of state backends
-are available -- one based on RocksDB, an embedded key/value store that keeps its working state on
-disk, and another heap-based state backend that keeps its working state in memory, on the Java heap.
-This heap-based state backend comes in two flavors: the FsStateBackend that persists its state
-snapshots to a distributed file system, and the MemoryStateBackend that uses the JobManager's heap.
-
-
-
-
-
Name
-
Working State
-
State Backup
-
Snapshotting
-
-
-
-
-
RocksDBStateBackend
-
Local disk (tmp dir)
-
Distributed file system
-
Full / Incremental
-
-
-
-
-
Supports state larger than available memory
-
Rule of thumb: 10x slower than heap-based backends
-
-
-
-
-
FsStateBackend
-
JVM Heap
-
Distributed file system
-
Full
-
-
-
-
-
Fast, requires large heap
-
Subject to GC
-
-
-
-
-
MemoryStateBackend
-
JVM Heap
-
JobManager JVM Heap
-
Full
-
-
-
-
-
Good for testing and experimentation with small state (locally)
-
-
-
-
-
-
-When working with state kept in a heap-based state backend, accesses and updates involve reading and
-writing objects on the heap. But for objects kept in the `RocksDBStateBackend`, accesses and updates
-involve serialization and deserialization, and so are much more expensive. But the amount of state
-you can have with RocksDB is limited only by the size of the local disk. Note also that only the
-`RocksDBStateBackend` is able to do incremental snapshotting, which is a significant benefit for
-applications with large amounts of slowly changing state.
-
-All of these state backends are able to do asynchronous snapshotting, meaning that they can take a
-snapshot without impeding the ongoing stream processing.
-
-{% top %}
-
-## State Snapshots
-
-### Definitions
-
-* _Snapshot_ -- a generic term referring to a global, consistent image of the state of a Flink job.
- A snapshot includes a pointer into each of the data sources (e.g., an offset into a file or Kafka
- partition), as well as a copy of the state from each of the job's stateful operators that resulted
- from having processed all of the events up to those positions in the sources.
-* _Checkpoint_ -- a snapshot taken automatically by Flink for the purpose of being able to recover
- from faults. Checkpoints can be incremental, and are optimized for being restored quickly.
-* _Externalized Checkpoint_ -- normally checkpoints are not intended to be manipulated by users.
- Flink retains only the _n_-most-recent checkpoints (_n_ being configurable) while a job is
- running, and deletes them when a job is cancelled. But you can configure them to be retained
- instead, in which case you can manually resume from them.
-* _Savepoint_ -- a snapshot triggered manually by a user (or an API call) for some operational
- purpose, such as a stateful redeploy/upgrade/rescaling operation. Savepoints are always complete,
- and are optimized for operational flexibility.
-
-### How does State Snapshotting Work?
-
-Flink uses a variant of the [Chandy-Lamport
-algorithm](https://en.wikipedia.org/wiki/Chandy-Lamport_algorithm) known as _asynchronous barrier
-snapshotting_.
-
-When a task manager is instructed by the checkpoint coordinator (part of the job manager) to begin a
-checkpoint, it has all of the sources record their offsets and insert numbered _checkpoint barriers_
-into their streams. These barriers flow through the job graph, indicating the part of the stream
-before and after each checkpoint.
-
-
-
-Checkpoint _n_ will contain the state of each operator that resulted from having consumed **every
-event before checkpoint barrier _n_, and none of the events after it**.
-
-As each operator in the job graph receives one of these barriers, it records its state. Operators
-with two input streams (such as a `CoProcessFunction`) perform _barrier alignment_ so that the
-snapshot will reflect the state resulting from consuming events from both input streams up to (but
-not past) both barriers.
-
-
-
-Flink's state backends use a copy-on-write mechanism to allow stream processing to continue
-unimpeded while older versions of the state are being asynchronously snapshotted. Only when the
-snapshots have been durably persisted will these older versions of the state be garbage collected.
-
-### Exactly Once Guarantees
-
-When things go wrong in a stream processing application, it is possible to have either lost, or
-duplicated results. With Flink, depending on the choices you make for your application and the
-cluster you run it on, any of these outcomes is possible:
-
-- Flink makes no effort to recover from failures (_at most once_)
-- Nothing is lost, but you may experience duplicated results (_at least once_)
-- Nothing is lost or duplicated (_exactly once_)
-
-Given that Flink recovers from faults by rewinding and replaying the source data streams, when the
-ideal situation is described as **exactly once** this does *not* mean that every event will be
-processed exactly once. Instead, it means that _every event will affect the state being managed by
-Flink exactly once_.
-
-Barrier alignment is only needed for providing exactly once guarantees. If you don't need this, you
-can gain some performance by configuring Flink to use `CheckpointingMode.AT_LEAST_ONCE`, which has
-the effect of disabling barrier alignment.
-
-### Exactly Once End-to-end
-
-To achieve exactly once end-to-end, so that every event from the sources affects the sinks exactly
-once, the following must be true:
-
-1. your sources must be replayable, and
-2. your sinks must be transactional (or idempotent)
-
-{% top %}
-
-## Hands-on
-
-The [Flink Operations Playground]({% link
-getting-started/docker-playgrounds/flink-operations-playground.zh.md %}) includes a section on
-[Observing Failure & Recovery]({% link
-getting-started/docker-playgrounds/flink-operations-playground.zh.md %}#observing-failure--recovery).
-
-{% top %}
-
-## Further Reading
-
-- [Stateful Stream Processing]({% link concepts/stateful-stream-processing.zh.md %})
-- [State Backends]({% link ops/state/state_backends.zh.md %})
-- [Fault Tolerance Guarantees of Data Sources and Sinks]({% link dev/connectors/guarantees.zh.md %})
-- [Enabling and Configuring Checkpointing]({% link dev/stream/state/checkpointing.zh.md %})
-- [Checkpoints]({% link ops/state/checkpoints.zh.md %})
-- [Savepoints]({% link ops/state/savepoints.zh.md %})
-- [Tuning Checkpoints and Large State]({% link ops/state/large_state_tuning.zh.md %})
-- [Monitoring Checkpointing]({% link monitoring/checkpoint_monitoring.zh.md %})
-- [Task Failure Recovery]({% link dev/task_failure_recovery.zh.md %})
-
-{% top %}
diff --git a/docs/training/index.md b/docs/training/index.md
deleted file mode 100644
index 8f00f9079d6f17..00000000000000
--- a/docs/training/index.md
+++ /dev/null
@@ -1,186 +0,0 @@
----
-title: Hands-on Training
-nav-id: training
-nav-pos: 2
-nav-title: ' Hands-on Training'
-nav-parent_id: root
-nav-show_overview: true
-always-expand: true
----
-
-
-* This will be replaced by the TOC
-{:toc}
-
-## Goals and Scope of this Training
-
-This training presents an introduction to Apache Flink that includes just enough to get you started
-writing scalable streaming ETL, analytics, and event-driven applications, while leaving out a lot of
-(ultimately important) details. The focus is on providing straightforward introductions to Flink's
-APIs for managing state and time, with the expectation that having mastered these fundamentals,
-you'll be much better equipped to pick up the rest of what you need to know from the more detailed
-reference documentation. The links at the end of each section will lead you to where you
-can learn more.
-
-Specifically, you will learn:
-
-- how to implement streaming data processing pipelines
-- how and why Flink manages state
-- how to use event time to consistently compute accurate analytics
-- how to build event-driven applications on continuous streams
-- how Flink is able to provide fault-tolerant, stateful stream processing with exactly-once semantics
-
-This training focuses on four critical concepts: continuous processing of streaming data, event
-time, stateful stream processing, and state snapshots. This page introduces these concepts.
-
-{% info Note %} Accompanying this training is a set of hands-on exercises that will
-guide you through learning how to work with the concepts being presented. A link to the relevant
-exercise is provided at the end of each section.
-
-{% top %}
-
-## Stream Processing
-
-Streams are data's natural habitat. Whether it is events from web servers, trades from a stock
-exchange, or sensor readings from a machine on a factory floor, data is created as part of a stream.
-But when you analyze data, you can either organize your processing around _bounded_ or _unbounded_
-streams, and which of these paradigms you choose has profound consequences.
-
-
-
-**Batch processing** is the paradigm at work when you process a bounded data stream. In this mode of
-operation you can choose to ingest the entire dataset before producing any results, which means that
-it is possible, for example, to sort the data, compute global statistics, or produce a final report
-that summarizes all of the input.
-
-**Stream processing**, on the other hand, involves unbounded data streams. Conceptually, at least,
-the input may never end, and so you are forced to continuously process the data as it arrives.
-
-In Flink, applications are composed of **streaming dataflows** that may be transformed by
-user-defined **operators**. These dataflows form directed graphs that start with one or more
-**sources**, and end in one or more **sinks**.
-
-
-
-Often there is a one-to-one correspondence between the transformations in the program and the
-operators in the dataflow. Sometimes, however, one transformation may consist of multiple operators.
-
-An application may consume real-time data from streaming sources such as message queues or
-distributed logs, like Apache Kafka or Kinesis. But flink can also consume bounded, historic data
-from a variety of data sources. Similarly, the streams of results being produced by a Flink
-application can be sent to a wide variety of systems that can be connected as sinks.
-
-
-
-### Parallel Dataflows
-
-Programs in Flink are inherently parallel and distributed. During execution, a
-*stream* has one or more **stream partitions**, and each *operator* has one or
-more **operator subtasks**. The operator subtasks are independent of one
-another, and execute in different threads and possibly on different machines or
-containers.
-
-The number of operator subtasks is the **parallelism** of that particular
-operator.
-Different operators of the same program may have different levels of
-parallelism.
-
-
-
-Streams can transport data between two operators in a *one-to-one* (or
-*forwarding*) pattern, or in a *redistributing* pattern:
-
- - **One-to-one** streams (for example between the *Source* and the *map()*
- operators in the figure above) preserve the partitioning and ordering of
- the elements. That means that subtask[1] of the *map()* operator will see
- the same elements in the same order as they were produced by subtask[1] of
- the *Source* operator.
-
- - **Redistributing** streams (as between *map()* and *keyBy/window* above, as
- well as between *keyBy/window* and *Sink*) change the partitioning of
- streams. Each *operator subtask* sends data to different target subtasks,
- depending on the selected transformation. Examples are *keyBy()* (which
- re-partitions by hashing the key), *broadcast()*, or *rebalance()* (which
- re-partitions randomly). In a *redistributing* exchange the ordering among
- the elements is only preserved within each pair of sending and receiving
- subtasks (for example, subtask[1] of *map()* and subtask[2] of
- *keyBy/window*). So, for example, the redistribution between the keyBy/window and
- the Sink operators shown above introduces non-determinism regarding the
- order in which the aggregated results for different keys arrive at the Sink.
-
-{% top %}
-
-## Timely Stream Processing
-
-For most streaming applications it is very valuable to be able re-process historic data with the
-same code that is used to process live data -- and to produce deterministic, consistent results,
-regardless.
-
-It can also be crucial to pay attention to the order in which events occurred, rather than the order
-in which they are delivered for processing, and to be able to reason about when a set of events is
-(or should be) complete. For example, consider the set of events involved in an e-commerce
-transaction, or financial trade.
-
-These requirements for timely stream processing can be met by using event time timestamps that are
-recorded in the data stream, rather than using the clocks of the machines processing the data.
-
-{% top %}
-
-## Stateful Stream Processing
-
-Flink's operations can be stateful. This means that how one event is handled can depend on the
-accumulated effect of all the events that came before it. State may be used for something simple,
-such as counting events per minute to display on a dashboard, or for something more complex, such as
-computing features for a fraud detection model.
-
-A Flink application is run in parallel on a distributed cluster. The various parallel instances of a
-given operator will execute independently, in separate threads, and in general will be running on
-different machines.
-
-The set of parallel instances of a stateful operator is effectively a sharded key-value store. Each
-parallel instance is responsible for handling events for a specific group of keys, and the state for
-those keys is kept locally.
-
-The diagram below shows a job running with a parallelism of two across the first three operators in
-the job graph, terminating in a sink that has a parallelism of one. The third operator is stateful,
-and you can see that a fully-connected network shuffle is occurring between the second and third
-operators. This is being done to partition the stream by some key, so that all of the events that
-need to be processed together, will be.
-
-
-
-State is always accessed locally, which helps Flink applications achieve high throughput and
-low-latency. You can choose to keep state on the JVM heap, or if it is too large, in efficiently
-organized on-disk data structures.
-
-
-
-{% top %}
-
-## Fault Tolerance via State Snapshots
-
-Flink is able to provide fault-tolerant, exactly-once semantics through a combination of state
-snapshots and stream replay. These snapshots capture the entire state of the distributed pipeline,
-recording offsets into the input queues as well as the state throughout the job graph that has
-resulted from having ingested the data up to that point. When a failure occurs, the sources are
-rewound, the state is restored, and processing is resumed. As depicted above, these state snapshots
-are captured asynchronously, without impeding the ongoing processing.
-
-{% top %}
diff --git a/docs/training/index.zh.md b/docs/training/index.zh.md
deleted file mode 100644
index 8f00f9079d6f17..00000000000000
--- a/docs/training/index.zh.md
+++ /dev/null
@@ -1,186 +0,0 @@
----
-title: Hands-on Training
-nav-id: training
-nav-pos: 2
-nav-title: ' Hands-on Training'
-nav-parent_id: root
-nav-show_overview: true
-always-expand: true
----
-
-
-* This will be replaced by the TOC
-{:toc}
-
-## Goals and Scope of this Training
-
-This training presents an introduction to Apache Flink that includes just enough to get you started
-writing scalable streaming ETL, analytics, and event-driven applications, while leaving out a lot of
-(ultimately important) details. The focus is on providing straightforward introductions to Flink's
-APIs for managing state and time, with the expectation that having mastered these fundamentals,
-you'll be much better equipped to pick up the rest of what you need to know from the more detailed
-reference documentation. The links at the end of each section will lead you to where you
-can learn more.
-
-Specifically, you will learn:
-
-- how to implement streaming data processing pipelines
-- how and why Flink manages state
-- how to use event time to consistently compute accurate analytics
-- how to build event-driven applications on continuous streams
-- how Flink is able to provide fault-tolerant, stateful stream processing with exactly-once semantics
-
-This training focuses on four critical concepts: continuous processing of streaming data, event
-time, stateful stream processing, and state snapshots. This page introduces these concepts.
-
-{% info Note %} Accompanying this training is a set of hands-on exercises that will
-guide you through learning how to work with the concepts being presented. A link to the relevant
-exercise is provided at the end of each section.
-
-{% top %}
-
-## Stream Processing
-
-Streams are data's natural habitat. Whether it is events from web servers, trades from a stock
-exchange, or sensor readings from a machine on a factory floor, data is created as part of a stream.
-But when you analyze data, you can either organize your processing around _bounded_ or _unbounded_
-streams, and which of these paradigms you choose has profound consequences.
-
-
-
-**Batch processing** is the paradigm at work when you process a bounded data stream. In this mode of
-operation you can choose to ingest the entire dataset before producing any results, which means that
-it is possible, for example, to sort the data, compute global statistics, or produce a final report
-that summarizes all of the input.
-
-**Stream processing**, on the other hand, involves unbounded data streams. Conceptually, at least,
-the input may never end, and so you are forced to continuously process the data as it arrives.
-
-In Flink, applications are composed of **streaming dataflows** that may be transformed by
-user-defined **operators**. These dataflows form directed graphs that start with one or more
-**sources**, and end in one or more **sinks**.
-
-
-
-Often there is a one-to-one correspondence between the transformations in the program and the
-operators in the dataflow. Sometimes, however, one transformation may consist of multiple operators.
-
-An application may consume real-time data from streaming sources such as message queues or
-distributed logs, like Apache Kafka or Kinesis. But flink can also consume bounded, historic data
-from a variety of data sources. Similarly, the streams of results being produced by a Flink
-application can be sent to a wide variety of systems that can be connected as sinks.
-
-
-
-### Parallel Dataflows
-
-Programs in Flink are inherently parallel and distributed. During execution, a
-*stream* has one or more **stream partitions**, and each *operator* has one or
-more **operator subtasks**. The operator subtasks are independent of one
-another, and execute in different threads and possibly on different machines or
-containers.
-
-The number of operator subtasks is the **parallelism** of that particular
-operator.
-Different operators of the same program may have different levels of
-parallelism.
-
-
-
-Streams can transport data between two operators in a *one-to-one* (or
-*forwarding*) pattern, or in a *redistributing* pattern:
-
- - **One-to-one** streams (for example between the *Source* and the *map()*
- operators in the figure above) preserve the partitioning and ordering of
- the elements. That means that subtask[1] of the *map()* operator will see
- the same elements in the same order as they were produced by subtask[1] of
- the *Source* operator.
-
- - **Redistributing** streams (as between *map()* and *keyBy/window* above, as
- well as between *keyBy/window* and *Sink*) change the partitioning of
- streams. Each *operator subtask* sends data to different target subtasks,
- depending on the selected transformation. Examples are *keyBy()* (which
- re-partitions by hashing the key), *broadcast()*, or *rebalance()* (which
- re-partitions randomly). In a *redistributing* exchange the ordering among
- the elements is only preserved within each pair of sending and receiving
- subtasks (for example, subtask[1] of *map()* and subtask[2] of
- *keyBy/window*). So, for example, the redistribution between the keyBy/window and
- the Sink operators shown above introduces non-determinism regarding the
- order in which the aggregated results for different keys arrive at the Sink.
-
-{% top %}
-
-## Timely Stream Processing
-
-For most streaming applications it is very valuable to be able re-process historic data with the
-same code that is used to process live data -- and to produce deterministic, consistent results,
-regardless.
-
-It can also be crucial to pay attention to the order in which events occurred, rather than the order
-in which they are delivered for processing, and to be able to reason about when a set of events is
-(or should be) complete. For example, consider the set of events involved in an e-commerce
-transaction, or financial trade.
-
-These requirements for timely stream processing can be met by using event time timestamps that are
-recorded in the data stream, rather than using the clocks of the machines processing the data.
-
-{% top %}
-
-## Stateful Stream Processing
-
-Flink's operations can be stateful. This means that how one event is handled can depend on the
-accumulated effect of all the events that came before it. State may be used for something simple,
-such as counting events per minute to display on a dashboard, or for something more complex, such as
-computing features for a fraud detection model.
-
-A Flink application is run in parallel on a distributed cluster. The various parallel instances of a
-given operator will execute independently, in separate threads, and in general will be running on
-different machines.
-
-The set of parallel instances of a stateful operator is effectively a sharded key-value store. Each
-parallel instance is responsible for handling events for a specific group of keys, and the state for
-those keys is kept locally.
-
-The diagram below shows a job running with a parallelism of two across the first three operators in
-the job graph, terminating in a sink that has a parallelism of one. The third operator is stateful,
-and you can see that a fully-connected network shuffle is occurring between the second and third
-operators. This is being done to partition the stream by some key, so that all of the events that
-need to be processed together, will be.
-
-
-
-State is always accessed locally, which helps Flink applications achieve high throughput and
-low-latency. You can choose to keep state on the JVM heap, or if it is too large, in efficiently
-organized on-disk data structures.
-
-
-
-{% top %}
-
-## Fault Tolerance via State Snapshots
-
-Flink is able to provide fault-tolerant, exactly-once semantics through a combination of state
-snapshots and stream replay. These snapshots capture the entire state of the distributed pipeline,
-recording offsets into the input queues as well as the state throughout the job graph that has
-resulted from having ingested the data up to that point. When a failure occurs, the sources are
-rewound, the state is restored, and processing is resumed. As depicted above, these state snapshots
-are captured asynchronously, without impeding the ongoing processing.
-
-{% top %}
diff --git a/docs/try-flink/datastream_api.md b/docs/try-flink/datastream_api.md
new file mode 100644
index 00000000000000..3d2e32366aae35
--- /dev/null
+++ b/docs/try-flink/datastream_api.md
@@ -0,0 +1,943 @@
+---
+title: "Fraud Detection with the DataStream API"
+nav-title: 'Fraud Detection with the DataStream API'
+nav-parent_id: try-flink
+nav-pos: 2
+---
+
+
+Apache Flink offers a DataStream API for building robust, stateful streaming applications.
+It provides fine-grained control over state and time, which allows for the implementation of advanced event-driven systems.
+In this step-by-step guide you'll learn how to build a stateful streaming application with Flink's DataStream API.
+
+* This will be replaced by the TOC
+{:toc}
+
+## What Are You Building?
+
+Credit card fraud is a growing concern in the digital age.
+Criminals steal credit card numbers by running scams or hacking into insecure systems.
+Stolen numbers are tested by making one or more small purchases, often for a dollar or less.
+If that works, they then make more significant purchases to get items they can sell or keep for themselves.
+
+In this tutorial, you will build a fraud detection system for alerting on suspicious credit card transactions.
+Using a simple set of rules, you will see how Flink allows us to implement advanced business logic and act in real-time.
+
+## Prerequisites
+
+This walkthrough assumes that you have some familiarity with Java or Scala, but you should be able to follow along even if you are coming from a different programming language.
+
+## Help, I’m Stuck!
+
+If you get stuck, check out the [community support resources](https://flink.apache.org/gettinghelp.html).
+In particular, Apache Flink's [user mailing list](https://flink.apache.org/community.html#mailing-lists) is consistently ranked as one of the most active of any Apache project and a great way to get help quickly.
+
+## How to Follow Along
+
+If you want to follow along, you will require a computer with:
+
+* Java 8 or 11
+* Maven
+
+A provided Flink Maven Archetype will create a skeleton project with all the necessary dependencies quickly, so you only need to focus on filling out the business logic.
+These dependencies include `flink-streaming-java` which is the core dependency for all Flink streaming applications and `flink-walkthrough-common` that has data generators and other classes specific to this walkthrough.
+
+{% panel **Note:** Each code block within this walkthrough may not contain the full surrounding class for brevity. The full code is available [at the bottom of the page](#final-application). %}
+
+
+ Note: For Maven 3.0 or higher, it is no longer possible to specify the repository (-DarchetypeCatalog) via the command line. For details about this change, please refer to Maven official document
+ If you wish to use the snapshot repository, you need to add a repository entry to your settings.xml. For example:
+{% highlight bash %}
+
+
+ apache
+
+
+
+ apache
+
+
+ apache-snapshots
+ https://repository.apache.org/content/repositories/snapshots/
+
+
+
+
+
+{% endhighlight %}
+
+{% endunless %}
+
+You can edit the `groupId`, `artifactId` and `package` if you like. With the above parameters,
+Maven will create a folder named `frauddetection` that contains a project with all the dependencies to complete this tutorial.
+After importing the project into your editor, you can find a file `FraudDetectionJob.java` (or `FraudDetectionJob.scala`) with the following code which you can run directly inside your IDE.
+Try setting break points through out the data stream and run the code in DEBUG mode to get a feeling for how everything works.
+
+
+
+## Breaking Down the Code
+
+Let's walk step-by-step through the code of these two files. The `FraudDetectionJob` class defines the data flow of the application and the `FraudDetector` class defines the business logic of the function that detects fraudulent transactions.
+
+We start describing how the Job is assembled in the `main` method of the `FraudDetectionJob` class.
+
+#### The Execution Environment
+
+The first line sets up your `StreamExecutionEnvironment`.
+The execution environment is how you set properties for your Job, create your sources, and finally trigger the execution of the Job.
+
+
+
+#### Creating a Source
+
+Sources ingest data from external systems, such as Apache Kafka, Rabbit MQ, or Apache Pulsar, into Flink Jobs.
+This walkthrough uses a source that generates an infinite stream of credit card transactions for you to process.
+Each transaction contains an account ID (`accountId`), timestamp (`timestamp`) of when the transaction occurred, and US$ amount (`amount`).
+The `name` attached to the source is just for debugging purposes, so if something goes wrong, we will know where the error originated.
+
+
+
+
+#### Partitioning Events & Detecting Fraud
+
+The `transactions` stream contains a lot of transactions from a large number of users, such that it needs to be processed in parallel by multiple fraud detection tasks. Since fraud occurs on a per-account basis, you must ensure that all transactions for the same account are processed by the same parallel task of the fraud detector operator.
+
+To ensure that the same physical task processes all records for a particular key, you can partition a stream using `DataStream#keyBy`.
+The `process()` call adds an operator that applies a function to each partitioned element in the stream.
+It is common to say the operator immediately after a `keyBy`, in this case `FraudDetector`, is executed within a _keyed context_.
+
+
+
+
+#### Outputting Results
+
+A sink writes a `DataStream` to an external system; such as Apache Kafka, Cassandra, and AWS Kinesis.
+The `AlertSink` logs each `Alert` record with log level **INFO**, instead of writing it to persistent storage, so you can easily see your results.
+
+
+
+#### Executing the Job
+
+Flink applications are built lazily and shipped to the cluster for execution only once fully formed.
+Call `StreamExecutionEnvironment#execute` to begin the execution of our Job and give it a name.
+
+
+
+#### The Fraud Detector
+
+The fraud detector is implemented as a `KeyedProcessFunction`.
+Its method `KeyedProcessFunction#processElement` is called for every transaction event.
+This first version produces an alert on every transaction, which some may say is overly conservative.
+
+The next steps of this tutorial will guide you to expand the fraud detector with more meaningful business logic.
+
+
+
+## Writing a Real Application (v1)
+
+For the first version, the fraud detector should output an alert for any account that makes a small transaction immediately followed by a large one. Where small is anything less than $1.00 and large is more than $500.
+Imagine your fraud detector processes the following stream of transactions for a particular account.
+
+
+
+
+
+Transactions 3 and 4 should be marked as fraudulent because it is a small transaction, $0.09, followed by a large one, $510.
+Alternatively, transactions 7, 8, and 9 are not fraud because the small amount of $0.02 is not immediately followed by the large one; instead, there is an intermediate transaction that breaks the pattern.
+
+To do this, the fraud detector must _remember_ information across events; a large transaction is only fraudulent if the previous one was small.
+Remembering information across events requires [state]({{ site.baseurl }}/concepts/glossary.html#managed-state), and that is why we decided to use a [KeyedProcessFunction]({{ site.baseurl }}/dev/stream/operators/process_function.html).
+It provides fine-grained control over both state and time, which will allow us to evolve our algorithm with more complex requirements throughout this walkthrough.
+
+The most straightforward implementation is a boolean flag that is set whenever a small transaction is processed.
+When a large transaction comes through, you can simply check if the flag is set for that account.
+
+However, merely implementing the flag as a member variable in the `FraudDetector` class will not work.
+Flink processes the transactions of multiple accounts with the same object instance of `FraudDetector`, which means if accounts A and B are routed through the same instance of `FraudDetector`, a transaction for account A could set the flag to true and then a transaction for account B could set off a false alert.
+We could of course use a data structure like a `Map` to keep track of the flags for individual keys, however, a simple member variable would not be fault-tolerant and all its information be lost in case of a failure.
+Hence, the fraud detector would possibly miss alerts if the application ever had to restart to recover from a failure.
+
+To address these challenges, Flink provides primitives for fault-tolerant state that are almost as easy to use as regular member variables.
+
+The most basic type of state in Flink is [ValueState]({{ site.baseurl }}/dev/stream/state/state.html#using-managed-keyed-state), a data type that adds fault tolerance to any variable it wraps.
+`ValueState` is a form of _keyed state_, meaning it is only available in operators that are applied in a _keyed context_; any operator immediately following `DataStream#keyBy`.
+A _keyed state_ of an operator is automatically scoped to the key of the record that is currently processed.
+In this example, the key is the account id for the current transaction (as declared by `keyBy()`), and `FraudDetector` maintains an independent state for each account.
+`ValueState` is created using a `ValueStateDescriptor` which contains metadata about how Flink should manage the variable. The state should be registered before the function starts processing data.
+The right hook for this is the `open()` method.
+
+
+
+`ValueState` is a wrapper class, similar to `AtomicReference` or `AtomicLong` in the Java standard library.
+It provides three methods for interacting with its contents; `update` sets the state, `value` gets the current value, and `clear` deletes its contents.
+If the state for a particular key is empty, such as at the beginning of an application or after calling `ValueState#clear`, then `ValueState#value` will return `null`.
+Modifications to the object returned by `ValueState#value` are not guaranteed to be recognized by the system, and so all changes must be performed with `ValueState#update`.
+Otherwise, fault tolerance is managed automatically by Flink under the hood, and so you can interact with it like with any standard variable.
+
+Below, you can see an example of how you can use a flag state to track potential fraudulent transactions.
+
+
+
+{% highlight java %}
+@Override
+public void processElement(
+ Transaction transaction,
+ Context context,
+ Collector collector) throws Exception {
+
+ // Get the current state for the current key
+ Boolean lastTransactionWasSmall = flagState.value();
+
+ // Check if the flag is set
+ if (lastTransactionWasSmall != null) {
+ if (transaction.getAmount() > LARGE_AMOUNT) {
+ // Output an alert downstream
+ Alert alert = new Alert();
+ alert.setId(transaction.getAccountId());
+
+ collector.collect(alert);
+ }
+
+ // Clean up our state
+ flagState.clear();
+ }
+
+ if (transaction.getAmount() < SMALL_AMOUNT) {
+ // Set the flag to true
+ flagState.update(true);
+ }
+}
+{% endhighlight %}
+
+
+
+{% highlight scala %}
+override def processElement(
+ transaction: Transaction,
+ context: KeyedProcessFunction[Long, Transaction, Alert]#Context,
+ collector: Collector[Alert]): Unit = {
+
+ // Get the current state for the current key
+ val lastTransactionWasSmall = flagState.value
+
+ // Check if the flag is set
+ if (lastTransactionWasSmall != null) {
+ if (transaction.getAmount > FraudDetector.LARGE_AMOUNT) {
+ // Output an alert downstream
+ val alert = new Alert
+ alert.setId(transaction.getAccountId)
+
+ collector.collect(alert)
+ }
+ // Clean up our state
+ flagState.clear()
+ }
+
+ if (transaction.getAmount < FraudDetector.SMALL_AMOUNT) {
+ // set the flag to true
+ flagState.update(true)
+ }
+}
+{% endhighlight %}
+
+
+
+For every transaction, the fraud detector checks the state of the flag for that account.
+Remember, `ValueState` is always scoped to the current key, i.e., account.
+If the flag is non-null, then the last transaction seen for that account was small, and so if the amount for this transaction is large, then the detector outputs a fraud alert.
+
+After that check, the flag state is unconditionally cleared.
+Either the current transaction caused a fraud alert, and the pattern is over, or the current transaction did not cause an alert, and the pattern is broken and needs to be restarted.
+
+Finally, the transaction amount is checked to see if it is small.
+If so, then the flag is set so that it can be checked by the next event.
+Notice that `ValueState` actually has three states, unset ( `null`), `true`, and `false`, because all `ValueState`'s are nullable.
+This job only makes use of unset ( `null`) and `true` to check whether the flag is set or not.
+
+## Fraud Detector v2: State + Time = ❤️
+
+Scammers don't wait long to make their large purchase to reduce the chances their test transaction is noticed.
+For example, suppose you wanted to set a 1 minute timeout to your fraud detector; i.e., in the previous example transactions 3 and 4 would only be considered fraud if they occurred within 1 minute of each other.
+Flink's `KeyedProcessFunction` allows you to set timers which invoke a callback method at some point in time in the future.
+
+Let's see how we can modify our Job to comply with our new requirements:
+
+* Whenever the flag is set to `true`, also set a timer for 1 minute in the future.
+* When the timer fires, reset the flag by clearing its state.
+* If the flag is ever cleared the timer should be canceled.
+
+To cancel a timer, you have to remember what time it is set for, and remembering implies state, so you will begin by creating a timer state along with your flag state.
+
+
+{% highlight scala %}
+@SerialVersionUID(1L)
+class FraudDetector extends KeyedProcessFunction[Long, Transaction, Alert] {
+
+ @transient private var flagState: ValueState[java.lang.Boolean] = _
+ @transient private var timerState: ValueState[java.lang.Long] = _
+
+ @throws[Exception]
+ override def open(parameters: Configuration): Unit = {
+ val flagDescriptor = new ValueStateDescriptor("flag", Types.BOOLEAN)
+ flagState = getRuntimeContext.getState(flagDescriptor)
+
+ val timerDescriptor = new ValueStateDescriptor("timer-state", Types.LONG)
+ timerState = getRuntimeContext.getState(timerDescriptor)
+ }
+{% endhighlight %}
+
+
+
+`KeyedProcessFunction#processElement` is called with a `Context` that contains a timer service.
+The timer service can be used to query the current time, register timers, and delete timers.
+With this, you can set a timer for 1 minute in the future every time the flag is set and store the timestamp in `timerState`.
+
+
+
+{% highlight java %}
+if (transaction.getAmount() < SMALL_AMOUNT) {
+ // set the flag to true
+ flagState.update(true);
+
+ // set the timer and timer state
+ long timer = context.timerService().currentProcessingTime() + ONE_MINUTE;
+ context.timerService().registerProcessingTimeTimer(timer);
+ timerState.update(timer);
+}
+{% endhighlight %}
+
+
+{% highlight scala %}
+if (transaction.getAmount < FraudDetector.SMALL_AMOUNT) {
+ // set the flag to true
+ flagState.update(true)
+
+ // set the timer and timer state
+ val timer = context.timerService.currentProcessingTime + FraudDetector.ONE_MINUTE
+ context.timerService.registerProcessingTimeTimer(timer)
+ timerState.update(timer)
+}
+{% endhighlight %}
+
+
+
+Processing time is wall clock time, and is determined by the system clock of the machine running the operator.
+
+When a timer fires, it calls `KeyedProcessFunction#onTimer`.
+Overriding this method is how you can implement your callback to reset the flag.
+
+
+
+Finally, to cancel the timer, you need to delete the registered timer and delete the timer state.
+You can wrap this in a helper method and call this method instead of `flagState.clear()`.
+
+
+
+{% highlight java %}
+private void cleanUp(Context ctx) throws Exception {
+ // delete timer
+ Long timer = timerState.value();
+ ctx.timerService().deleteProcessingTimeTimer(timer);
+
+ // clean up all state
+ timerState.clear();
+ flagState.clear();
+}
+{% endhighlight %}
+
+
+
+{% highlight scala %}
+@throws[Exception]
+private def cleanUp(ctx: KeyedProcessFunction[Long, Transaction, Alert]#Context): Unit = {
+ // delete timer
+ val timer = timerState.value
+ ctx.timerService.deleteProcessingTimeTimer(timer)
+
+ // clean up all states
+ timerState.clear()
+ flagState.clear()
+}
+{% endhighlight %}
+
+
+
+And that's it, a fully functional, stateful, distributed streaming application!
+
+## Final Application
+
+
+
+{% highlight java %}
+package spendreport;
+
+import org.apache.flink.api.common.state.ValueState;
+import org.apache.flink.api.common.state.ValueStateDescriptor;
+import org.apache.flink.api.common.typeinfo.Types;
+import org.apache.flink.configuration.Configuration;
+import org.apache.flink.streaming.api.functions.KeyedProcessFunction;
+import org.apache.flink.util.Collector;
+import org.apache.flink.walkthrough.common.entity.Alert;
+import org.apache.flink.walkthrough.common.entity.Transaction;
+
+public class FraudDetector extends KeyedProcessFunction {
+
+ private static final long serialVersionUID = 1L;
+
+ private static final double SMALL_AMOUNT = 1.00;
+ private static final double LARGE_AMOUNT = 500.00;
+ private static final long ONE_MINUTE = 60 * 1000;
+
+ private transient ValueState flagState;
+ private transient ValueState timerState;
+
+ @Override
+ public void open(Configuration parameters) {
+ ValueStateDescriptor flagDescriptor = new ValueStateDescriptor<>(
+ "flag",
+ Types.BOOLEAN);
+ flagState = getRuntimeContext().getState(flagDescriptor);
+
+ ValueStateDescriptor timerDescriptor = new ValueStateDescriptor<>(
+ "timer-state",
+ Types.LONG);
+ timerState = getRuntimeContext().getState(timerDescriptor);
+ }
+
+ @Override
+ public void processElement(
+ Transaction transaction,
+ Context context,
+ Collector collector) throws Exception {
+
+ // Get the current state for the current key
+ Boolean lastTransactionWasSmall = flagState.value();
+
+ // Check if the flag is set
+ if (lastTransactionWasSmall != null) {
+ if (transaction.getAmount() > LARGE_AMOUNT) {
+ //Output an alert downstream
+ Alert alert = new Alert();
+ alert.setId(transaction.getAccountId());
+
+ collector.collect(alert);
+ }
+ // Clean up our state
+ cleanUp(context);
+ }
+
+ if (transaction.getAmount() < SMALL_AMOUNT) {
+ // set the flag to true
+ flagState.update(true);
+
+ long timer = context.timerService().currentProcessingTime() + ONE_MINUTE;
+ context.timerService().registerProcessingTimeTimer(timer);
+
+ timerState.update(timer);
+ }
+ }
+
+ @Override
+ public void onTimer(long timestamp, OnTimerContext ctx, Collector out) {
+ // remove flag after 1 minute
+ timerState.clear();
+ flagState.clear();
+ }
+
+ private void cleanUp(Context ctx) throws Exception {
+ // delete timer
+ Long timer = timerState.value();
+ ctx.timerService().deleteProcessingTimeTimer(timer);
+
+ // clean up all state
+ timerState.clear();
+ flagState.clear();
+ }
+}
+{% endhighlight %}
+
+
+
+{% highlight scala %}
+package spendreport
+
+import org.apache.flink.api.common.state.{ValueState, ValueStateDescriptor}
+import org.apache.flink.api.scala.typeutils.Types
+import org.apache.flink.configuration.Configuration
+import org.apache.flink.streaming.api.functions.KeyedProcessFunction
+import org.apache.flink.util.Collector
+import org.apache.flink.walkthrough.common.entity.Alert
+import org.apache.flink.walkthrough.common.entity.Transaction
+
+object FraudDetector {
+ val SMALL_AMOUNT: Double = 1.00
+ val LARGE_AMOUNT: Double = 500.00
+ val ONE_MINUTE: Long = 60 * 1000L
+}
+
+@SerialVersionUID(1L)
+class FraudDetector extends KeyedProcessFunction[Long, Transaction, Alert] {
+
+ @transient private var flagState: ValueState[java.lang.Boolean] = _
+ @transient private var timerState: ValueState[java.lang.Long] = _
+
+ @throws[Exception]
+ override def open(parameters: Configuration): Unit = {
+ val flagDescriptor = new ValueStateDescriptor("flag", Types.BOOLEAN)
+ flagState = getRuntimeContext.getState(flagDescriptor)
+
+ val timerDescriptor = new ValueStateDescriptor("timer-state", Types.LONG)
+ timerState = getRuntimeContext.getState(timerDescriptor)
+ }
+
+ override def processElement(
+ transaction: Transaction,
+ context: KeyedProcessFunction[Long, Transaction, Alert]#Context,
+ collector: Collector[Alert]): Unit = {
+
+ // Get the current state for the current key
+ val lastTransactionWasSmall = flagState.value
+
+ // Check if the flag is set
+ if (lastTransactionWasSmall != null) {
+ if (transaction.getAmount > FraudDetector.LARGE_AMOUNT) {
+ // Output an alert downstream
+ val alert = new Alert
+ alert.setId(transaction.getAccountId)
+
+ collector.collect(alert)
+ }
+ // Clean up our state
+ cleanUp(context)
+ }
+
+ if (transaction.getAmount < FraudDetector.SMALL_AMOUNT) {
+ // set the flag to true
+ flagState.update(true)
+ val timer = context.timerService.currentProcessingTime + FraudDetector.ONE_MINUTE
+
+ context.timerService.registerProcessingTimeTimer(timer)
+ timerState.update(timer)
+ }
+ }
+
+ override def onTimer(
+ timestamp: Long,
+ ctx: KeyedProcessFunction[Long, Transaction, Alert]#OnTimerContext,
+ out: Collector[Alert]): Unit = {
+ // remove flag after 1 minute
+ timerState.clear()
+ flagState.clear()
+ }
+
+ @throws[Exception]
+ private def cleanUp(ctx: KeyedProcessFunction[Long, Transaction, Alert]#Context): Unit = {
+ // delete timer
+ val timer = timerState.value
+ ctx.timerService.deleteProcessingTimeTimer(timer)
+
+ // clean up all states
+ timerState.clear()
+ flagState.clear()
+ }
+}
+{% endhighlight %}
+
+
+
+### Expected Output
+
+Running this code with the provided `TransactionSource` will emit fraud alerts for account 3.
+You should see the following output in your task manager logs:
+
+{% highlight bash %}
+2019-08-19 14:22:06,220 INFO org.apache.flink.walkthrough.common.sink.AlertSink - Alert{id=3}
+2019-08-19 14:22:11,383 INFO org.apache.flink.walkthrough.common.sink.AlertSink - Alert{id=3}
+2019-08-19 14:22:16,551 INFO org.apache.flink.walkthrough.common.sink.AlertSink - Alert{id=3}
+2019-08-19 14:22:21,723 INFO org.apache.flink.walkthrough.common.sink.AlertSink - Alert{id=3}
+2019-08-19 14:22:26,896 INFO org.apache.flink.walkthrough.common.sink.AlertSink - Alert{id=3}
+{% endhighlight %}
diff --git a/docs/try-flink/datastream_api.zh.md b/docs/try-flink/datastream_api.zh.md
new file mode 100644
index 00000000000000..5e9e27ac237b7d
--- /dev/null
+++ b/docs/try-flink/datastream_api.zh.md
@@ -0,0 +1,945 @@
+---
+title: "基于 DataStream API 实现欺诈检测"
+nav-title: '基于 DataStream API 实现欺诈检测'
+nav-parent_id: try-flink
+nav-pos: 2
+---
+
+
+Apache Flink 提供了 DataStream API 来实现稳定可靠的、有状态的流处理应用程序。
+Flink 支持对状态和时间的细粒度控制,以此来实现复杂的事件驱动数据处理系统。
+这个入门指导手册讲述了如何通过 Flink DataStream API 来实现一个有状态流处理程序。
+
+* This will be replaced by the TOC
+{:toc}
+
+## 你要搭建一个什么系统
+
+在当今数字时代,信用卡欺诈行为越来越被重视。
+罪犯可以通过诈骗或者入侵安全级别较低系统来盗窃信用卡卡号。
+用盗得的信用卡进行很小额度的例如一美元或者更小额度的消费进行测试。
+如果测试消费成功,那么他们就会用这个信用卡进行大笔消费,来购买一些他们希望得到的,或者可以倒卖的财物。
+
+在这个教程中,你将会建立一个针对可疑信用卡交易行为的反欺诈检测系统。
+通过使用一组简单的规则,你将了解到 Flink 如何为我们实现复杂业务逻辑并实时执行。
+
+## 准备条件
+
+这个代码练习假定你对 Java 或 Scala 有一定的了解,当然,如果你之前使用的是其他开发语言,你也应该能够跟随本教程进行学习。
+
+## 困难求助
+
+如果遇到困难,可以参考 [社区支持资源](https://flink.apache.org/zh/gettinghelp.html)。
+当然也可以在邮件列表提问,Flink 的 [用户邮件列表](https://flink.apache.org/zh/community.html#mailing-lists) 一直被评为所有Apache项目中最活跃的一个,这也是快速获得帮助的好方法。
+
+## 怎样跟着教程练习
+
+首先,你需要在你的电脑上准备以下环境:
+
+* Java 8 or 11
+* Maven
+
+一个准备好的 Flink Maven Archetype 能够快速创建一个包含了必要依赖的 Flink 程序骨架,基于此,你可以把精力集中在编写业务逻辑上即可。
+这些已包含的依赖包括 `flink-streaming-java`、`flink-walkthrough-common` 等,他们分别是 Flink 应用程序的核心依赖项和这个代码练习需要的数据生成器,当然还包括其他本代码练习所依赖的类。
+
+{% panel **说明:** 为简洁起见,本练习中的代码块中可能不包含完整的类路径。完整的类路径可以在文档底部 [链接](#完整的程序) 中找到。 %}
+
+
+{% highlight java %}
+@Override
+public void processElement(
+ Transaction transaction,
+ Context context,
+ Collector collector) throws Exception {
+
+ // Get the current state for the current key
+ Boolean lastTransactionWasSmall = flagState.value();
+
+ // Check if the flag is set
+ if (lastTransactionWasSmall != null) {
+ if (transaction.getAmount() > LARGE_AMOUNT) {
+ // Output an alert downstream
+ Alert alert = new Alert();
+ alert.setId(transaction.getAccountId());
+
+ collector.collect(alert);
+ }
+
+ // Clean up our state
+ flagState.clear();
+ }
+
+ if (transaction.getAmount() < SMALL_AMOUNT) {
+ // Set the flag to true
+ flagState.update(true);
+ }
+}
+{% endhighlight %}
+
+
+
+{% highlight scala %}
+override def processElement(
+ transaction: Transaction,
+ context: KeyedProcessFunction[Long, Transaction, Alert]#Context,
+ collector: Collector[Alert]): Unit = {
+
+ // Get the current state for the current key
+ val lastTransactionWasSmall = flagState.value
+
+ // Check if the flag is set
+ if (lastTransactionWasSmall != null) {
+ if (transaction.getAmount > FraudDetector.LARGE_AMOUNT) {
+ // Output an alert downstream
+ val alert = new Alert
+ alert.setId(transaction.getAccountId)
+
+ collector.collect(alert)
+ }
+ // Clean up our state
+ flagState.clear()
+ }
+
+ if (transaction.getAmount < FraudDetector.SMALL_AMOUNT) {
+ // set the flag to true
+ flagState.update(true)
+ }
+}
+{% endhighlight %}
+
+{% highlight java %}
+if (transaction.getAmount() < SMALL_AMOUNT) {
+ // set the flag to true
+ flagState.update(true);
+
+ // set the timer and timer state
+ long timer = context.timerService().currentProcessingTime() + ONE_MINUTE;
+ context.timerService().registerProcessingTimeTimer(timer);
+ timerState.update(timer);
+}
+{% endhighlight %}
+
+
+{% highlight scala %}
+if (transaction.getAmount < FraudDetector.SMALL_AMOUNT) {
+ // set the flag to true
+ flagState.update(true)
+
+ // set the timer and timer state
+ val timer = context.timerService.currentProcessingTime + FraudDetector.ONE_MINUTE
+ context.timerService.registerProcessingTimeTimer(timer)
+ timerState.update(timer)
+}
+{% endhighlight %}
+
+{% highlight java %}
+private void cleanUp(Context ctx) throws Exception {
+ // delete timer
+ Long timer = timerState.value();
+ ctx.timerService().deleteProcessingTimeTimer(timer);
+
+ // clean up all state
+ timerState.clear();
+ flagState.clear();
+}
+{% endhighlight %}
+
+
+
+{% highlight scala %}
+@throws[Exception]
+private def cleanUp(ctx: KeyedProcessFunction[Long, Transaction, Alert]#Context): Unit = {
+ // delete timer
+ val timer = timerState.value
+ ctx.timerService.deleteProcessingTimeTimer(timer)
+
+ // clean up all states
+ timerState.clear()
+ flagState.clear()
+}
+{% endhighlight %}
+
+
+
+这就是一个功能完备的,有状态的分布式流处理程序了。
+
+## 完整的程序
+
+
+
+{% highlight java %}
+package spendreport;
+
+import org.apache.flink.api.common.state.ValueState;
+import org.apache.flink.api.common.state.ValueStateDescriptor;
+import org.apache.flink.api.common.typeinfo.Types;
+import org.apache.flink.configuration.Configuration;
+import org.apache.flink.streaming.api.functions.KeyedProcessFunction;
+import org.apache.flink.util.Collector;
+import org.apache.flink.walkthrough.common.entity.Alert;
+import org.apache.flink.walkthrough.common.entity.Transaction;
+
+public class FraudDetector extends KeyedProcessFunction {
+
+ private static final long serialVersionUID = 1L;
+
+ private static final double SMALL_AMOUNT = 1.00;
+ private static final double LARGE_AMOUNT = 500.00;
+ private static final long ONE_MINUTE = 60 * 1000;
+
+ private transient ValueState flagState;
+ private transient ValueState timerState;
+
+ @Override
+ public void open(Configuration parameters) {
+ ValueStateDescriptor flagDescriptor = new ValueStateDescriptor<>(
+ "flag",
+ Types.BOOLEAN);
+ flagState = getRuntimeContext().getState(flagDescriptor);
+
+ ValueStateDescriptor timerDescriptor = new ValueStateDescriptor<>(
+ "timer-state",
+ Types.LONG);
+ timerState = getRuntimeContext().getState(timerDescriptor);
+ }
+
+ @Override
+ public void processElement(
+ Transaction transaction,
+ Context context,
+ Collector collector) throws Exception {
+
+ // Get the current state for the current key
+ Boolean lastTransactionWasSmall = flagState.value();
+
+ // Check if the flag is set
+ if (lastTransactionWasSmall != null) {
+ if (transaction.getAmount() > LARGE_AMOUNT) {
+ //Output an alert downstream
+ Alert alert = new Alert();
+ alert.setId(transaction.getAccountId());
+
+ collector.collect(alert);
+ }
+ // Clean up our state
+ cleanUp(context);
+ }
+
+ if (transaction.getAmount() < SMALL_AMOUNT) {
+ // set the flag to true
+ flagState.update(true);
+
+ long timer = context.timerService().currentProcessingTime() + ONE_MINUTE;
+ context.timerService().registerProcessingTimeTimer(timer);
+
+ timerState.update(timer);
+ }
+ }
+
+ @Override
+ public void onTimer(long timestamp, OnTimerContext ctx, Collector out) {
+ // remove flag after 1 minute
+ timerState.clear();
+ flagState.clear();
+ }
+
+ private void cleanUp(Context ctx) throws Exception {
+ // delete timer
+ Long timer = timerState.value();
+ ctx.timerService().deleteProcessingTimeTimer(timer);
+
+ // clean up all state
+ timerState.clear();
+ flagState.clear();
+ }
+}
+{% endhighlight %}
+
+
+
+{% highlight scala %}
+package spendreport
+
+import org.apache.flink.api.common.state.{ValueState, ValueStateDescriptor}
+import org.apache.flink.api.scala.typeutils.Types
+import org.apache.flink.configuration.Configuration
+import org.apache.flink.streaming.api.functions.KeyedProcessFunction
+import org.apache.flink.util.Collector
+import org.apache.flink.walkthrough.common.entity.Alert
+import org.apache.flink.walkthrough.common.entity.Transaction
+
+object FraudDetector {
+ val SMALL_AMOUNT: Double = 1.00
+ val LARGE_AMOUNT: Double = 500.00
+ val ONE_MINUTE: Long = 60 * 1000L
+}
+
+@SerialVersionUID(1L)
+class FraudDetector extends KeyedProcessFunction[Long, Transaction, Alert] {
+
+ @transient private var flagState: ValueState[java.lang.Boolean] = _
+ @transient private var timerState: ValueState[java.lang.Long] = _
+
+ @throws[Exception]
+ override def open(parameters: Configuration): Unit = {
+ val flagDescriptor = new ValueStateDescriptor("flag", Types.BOOLEAN)
+ flagState = getRuntimeContext.getState(flagDescriptor)
+
+ val timerDescriptor = new ValueStateDescriptor("timer-state", Types.LONG)
+ timerState = getRuntimeContext.getState(timerDescriptor)
+ }
+
+ override def processElement(
+ transaction: Transaction,
+ context: KeyedProcessFunction[Long, Transaction, Alert]#Context,
+ collector: Collector[Alert]): Unit = {
+
+ // Get the current state for the current key
+ val lastTransactionWasSmall = flagState.value
+
+ // Check if the flag is set
+ if (lastTransactionWasSmall != null) {
+ if (transaction.getAmount > FraudDetector.LARGE_AMOUNT) {
+ // Output an alert downstream
+ val alert = new Alert
+ alert.setId(transaction.getAccountId)
+
+ collector.collect(alert)
+ }
+ // Clean up our state
+ cleanUp(context)
+ }
+
+ if (transaction.getAmount < FraudDetector.SMALL_AMOUNT) {
+ // set the flag to true
+ flagState.update(true)
+ val timer = context.timerService.currentProcessingTime + FraudDetector.ONE_MINUTE
+
+ context.timerService.registerProcessingTimeTimer(timer)
+ timerState.update(timer)
+ }
+ }
+
+ override def onTimer(
+ timestamp: Long,
+ ctx: KeyedProcessFunction[Long, Transaction, Alert]#OnTimerContext,
+ out: Collector[Alert]): Unit = {
+ // remove flag after 1 minute
+ timerState.clear()
+ flagState.clear()
+ }
+
+ @throws[Exception]
+ private def cleanUp(ctx: KeyedProcessFunction[Long, Transaction, Alert]#Context): Unit = {
+ // delete timer
+ val timer = timerState.value
+ ctx.timerService.deleteProcessingTimeTimer(timer)
+
+ // clean up all states
+ timerState.clear()
+ flagState.clear()
+ }
+}
+{% endhighlight %}
+
+
+
+### 期望的结果
+
+使用已准备好的 `TransactionSource` 数据源运行这个代码,将会检测到账户 3 的欺诈行为,并输出报警信息。
+你将能够在你的 task manager 的日志中看到下边输出:
+
+{% highlight bash %}
+2019-08-19 14:22:06,220 INFO org.apache.flink.walkthrough.common.sink.AlertSink - Alert{id=3}
+2019-08-19 14:22:11,383 INFO org.apache.flink.walkthrough.common.sink.AlertSink - Alert{id=3}
+2019-08-19 14:22:16,551 INFO org.apache.flink.walkthrough.common.sink.AlertSink - Alert{id=3}
+2019-08-19 14:22:21,723 INFO org.apache.flink.walkthrough.common.sink.AlertSink - Alert{id=3}
+2019-08-19 14:22:26,896 INFO org.apache.flink.walkthrough.common.sink.AlertSink - Alert{id=3}
+{% endhighlight %}
diff --git a/docs/try-flink/flink-operations-playground.md b/docs/try-flink/flink-operations-playground.md
new file mode 100644
index 00000000000000..67e37d912b5322
--- /dev/null
+++ b/docs/try-flink/flink-operations-playground.md
@@ -0,0 +1,828 @@
+---
+title: "Flink Operations Playground"
+nav-title: 'Flink Operations Playground'
+nav-parent_id: try-flink
+nav-pos: 5
+---
+
+
+There are many ways to deploy and operate Apache Flink in various environments. Regardless of this
+variety, the fundamental building blocks of a Flink Cluster remain the same, and similar
+operational principles apply.
+
+In this playground, you will learn how to manage and run Flink Jobs. You will see how to deploy and
+monitor an application, experience how Flink recovers from Job failure, and perform everyday
+operational tasks like upgrades and rescaling.
+
+{% if site.version contains "SNAPSHOT" %}
+
+
+ NOTE: The Apache Flink Docker images used for this playground are only available for
+ released versions of Apache Flink.
+
+ Since you are currently looking at the latest SNAPSHOT
+ version of the documentation, all version references below will not work.
+ Please switch the documentation to the latest released version via the release picker which you
+ find on the left side below the menu.
+
+{% endif %}
+
+* This will be replaced by the TOC
+{:toc}
+
+## Anatomy of this Playground
+
+This playground consists of a long living
+[Flink Session Cluster]({{ site.baseurl }}/concepts/glossary.html#flink-session-cluster) and a Kafka
+Cluster.
+
+A Flink Cluster always consists of a
+[JobManager]({{ site.baseurl }}/concepts/glossary.html#flink-jobmanager) and one or more
+[Flink TaskManagers]({{ site.baseurl }}/concepts/glossary.html#flink-taskmanager). The JobManager
+is responsible for handling [Job]({{ site.baseurl }}/concepts/glossary.html#flink-job) submissions,
+the supervision of Jobs as well as resource management. The Flink TaskManagers are the worker
+processes and are responsible for the execution of the actual
+[Tasks]({{ site.baseurl }}/concepts/glossary.html#task) which make up a Flink Job. In this
+playground you will start with a single TaskManager, but scale out to more TaskManagers later.
+Additionally, this playground comes with a dedicated *client* container, which we use to submit the
+Flink Job initially and to perform various operational tasks later on. The *client* container is not
+needed by the Flink Cluster itself but only included for ease of use.
+
+The Kafka Cluster consists of a Zookeeper server and a Kafka Broker.
+
+
+
+When the playground is started a Flink Job called *Flink Event Count* will be submitted to the
+JobManager. Additionally, two Kafka Topics *input* and *output* are created.
+
+
+
+The Job consumes `ClickEvent`s from the *input* topic, each with a `timestamp` and a `page`. The
+events are then keyed by `page` and counted in 15 second
+[windows]({{ site.baseurl }}/dev/stream/operators/windows.html). The results are written to the
+*output* topic.
+
+There are six different pages and we generate 1000 click events per page and 15 seconds. Hence, the
+output of the Flink job should show 1000 views per page and window.
+
+{% top %}
+
+## Starting the Playground
+
+The playground environment is set up in just a few steps. We will walk you through the necessary
+commands and show how to validate that everything is running correctly.
+
+We assume that you have [Docker](https://docs.docker.com/) (1.12+) and
+[docker-compose](https://docs.docker.com/compose/) (2.1+) installed on your machine.
+
+The required configuration files are available in the
+[flink-playgrounds](https://github.com/apache/flink-playgrounds) repository. Check it out and spin
+up the environment:
+
+{% highlight bash %}
+git clone --branch release-{{ site.version_title }} https://github.com/apache/flink-playgrounds.git
+cd flink-playgrounds/operations-playground
+docker-compose build
+docker-compose up -d
+{% endhighlight %}
+
+Afterwards, you can inspect the running Docker containers with the following command:
+
+{% highlight bash %}
+docker-compose ps
+
+ Name Command State Ports
+-----------------------------------------------------------------------------------------------------------------------------
+operations-playground_clickevent-generator_1 /docker-entrypoint.sh java ... Up 6123/tcp, 8081/tcp
+operations-playground_client_1 /docker-entrypoint.sh flin ... Exit 0
+operations-playground_jobmanager_1 /docker-entrypoint.sh jobm ... Up 6123/tcp, 0.0.0.0:8081->8081/tcp
+operations-playground_kafka_1 start-kafka.sh Up 0.0.0.0:9094->9094/tcp
+operations-playground_taskmanager_1 /docker-entrypoint.sh task ... Up 6123/tcp, 8081/tcp
+operations-playground_zookeeper_1 /bin/sh -c /usr/sbin/sshd ... Up 2181/tcp, 22/tcp, 2888/tcp, 3888/tcp
+{% endhighlight %}
+
+This indicates that the client container has successfully submitted the Flink Job (`Exit 0`) and all
+cluster components as well as the data generator are running (`Up`).
+
+You can stop the playground environment by calling:
+
+{% highlight bash %}
+docker-compose down -v
+{% endhighlight %}
+
+## Entering the Playground
+
+There are many things you can try and check out in this playground. In the following two sections we
+will show you how to interact with the Flink Cluster and demonstrate some of Flink's key features.
+
+### Flink WebUI
+
+The most natural starting point to observe your Flink Cluster is the WebUI exposed under
+[http://localhost:8081](http://localhost:8081). If everything went well, you'll see that the cluster initially consists of
+one TaskManager and executes a Job called *Click Event Count*.
+
+
+
+The Flink WebUI contains a lot of useful and interesting information about your Flink Cluster and
+its Jobs (JobGraph, Metrics, Checkpointing Statistics, TaskManager Status,...).
+
+### Logs
+
+**JobManager**
+
+The JobManager logs can be tailed via `docker-compose`.
+
+{% highlight bash %}
+docker-compose logs -f jobmanager
+{% endhighlight %}
+
+After the initial startup you should mainly see log messages for every checkpoint completion.
+
+**TaskManager**
+
+The TaskManager log can be tailed in the same way.
+{% highlight bash %}
+docker-compose logs -f taskmanager
+{% endhighlight %}
+
+After the initial startup you should mainly see log messages for every checkpoint completion.
+
+### Flink CLI
+
+The [Flink CLI]({{ site.baseurl }}/ops/cli.html) can be used from within the client container. For
+example, to print the `help` message of the Flink CLI you can run
+{% highlight bash%}
+docker-compose run --no-deps client flink --help
+{% endhighlight %}
+
+### Flink REST API
+
+The [Flink REST API]({{ site.baseurl }}/monitoring/rest_api.html#api) is exposed via
+`localhost:8081` on the host or via `jobmanager:8081` from the client container, e.g. to list all
+currently running jobs, you can run:
+{% highlight bash%}
+curl localhost:8081/jobs
+{% endhighlight %}
+
+{% if site.version contains "SNAPSHOT" %}
+
+ Note: If the curl command is not available on your machine, you can run it from the client
+ container (similar to the Flink CLI):
+{% highlight bash%}
+docker-compose run --no-deps client curl jobmanager:8081/jobs
+{% endhighlight %}
+
+{% endif %}
+
+### Kafka Topics
+
+You can look at the records that are written to the Kafka Topics by running
+{% highlight bash%}
+//input topic (1000 records/s)
+docker-compose exec kafka kafka-console-consumer.sh \
+ --bootstrap-server localhost:9092 --topic input
+
+//output topic (24 records/min)
+docker-compose exec kafka kafka-console-consumer.sh \
+ --bootstrap-server localhost:9092 --topic output
+{% endhighlight %}
+
+{% top %}
+
+## Time to Play!
+
+Now that you learned how to interact with Flink and the Docker containers, let's have a look at
+some common operational tasks that you can try out on our playground.
+All of these tasks are independent of each other, i.e. you can perform them in any order.
+Most tasks can be executed via the [CLI](#flink-cli) and the [REST API](#flink-rest-api).
+
+### Listing Running Jobs
+
+
+
+The JobID is assigned to a Job upon submission and is needed to perform actions on the Job via the
+CLI or REST API.
+
+### Observing Failure & Recovery
+
+Flink provides exactly-once processing guarantees under (partial) failure. In this playground you
+can observe and - to some extent - verify this behavior.
+
+#### Step 1: Observing the Output
+
+As described [above](#anatomy-of-this-playground), the events in this playground are generate such
+that each window contains exactly one thousand records. So, in order to verify that Flink
+successfully recovers from a TaskManager failure without data loss or duplication you can tail the
+output topic and check that - after recovery - all windows are present and the count is correct.
+
+For this, start reading from the *output* topic and leave this command running until after
+recovery (Step 3).
+
+{% highlight bash%}
+docker-compose exec kafka kafka-console-consumer.sh \
+ --bootstrap-server localhost:9092 --topic output
+{% endhighlight %}
+
+#### Step 2: Introducing a Fault
+
+In order to simulate a partial failure you can kill a TaskManager. In a production setup, this
+could correspond to a loss of the TaskManager process, the TaskManager machine or simply a transient
+exception being thrown from the framework or user code (e.g. due to the temporary unavailability of
+an external resource).
+
+{% highlight bash%}
+docker-compose kill taskmanager
+{% endhighlight %}
+
+After a few seconds, the JobManager will notice the loss of the TaskManager, cancel the affected Job, and
+immediately resubmit it for recovery.
+When the Job gets restarted, its tasks remain in the `SCHEDULED` state, which is indicated by the
+purple colored squares (see screenshot below).
+
+
+
+
+ Note: Even though the tasks of the job are in SCHEDULED state and not RUNNING yet, the overall
+ status of a Job is shown as RUNNING.
+
+
+At this point, the tasks of the Job cannot move from the `SCHEDULED` state to `RUNNING` because there
+are no resources (TaskSlots provided by TaskManagers) to the run the tasks.
+Until a new TaskManager becomes available, the Job will go through a cycle of cancellations and resubmissions.
+
+In the meantime, the data generator keeps pushing `ClickEvent`s into the *input* topic. This is
+similar to a real production setup where data is produced while the Job to process it is down.
+
+#### Step 3: Recovery
+
+Once you restart the TaskManager, it reconnects to the JobManager.
+
+{% highlight bash%}
+docker-compose up -d taskmanager
+{% endhighlight %}
+
+When the JobManager is notified about the new TaskManager, it schedules the tasks of the
+recovering Job to the newly available TaskSlots. Upon restart, the tasks recover their state from
+the last successful [checkpoint]({{ site.baseurl }}/learn-flink/fault_tolerance.html) that was taken
+before the failure and switch to the `RUNNING` state.
+
+The Job will quickly process the full backlog of input events (accumulated during the outage)
+from Kafka and produce output at a much higher rate (> 24 records/minute) until it reaches
+the head of the stream. In the *output* you will see that all keys (`page`s) are present for all time
+windows and that every count is exactly one thousand. Since we are using the
+[FlinkKafkaProducer]({{ site.baseurl }}/dev/connectors/kafka.html#kafka-producers-and-fault-tolerance)
+in its "at-least-once" mode, there is a chance that you will see some duplicate output records.
+
+
+ Note: Most production setups rely on a resource manager (Kubernetes, Yarn, Mesos) to
+ automatically restart failed processes.
+
+
+### Upgrading & Rescaling a Job
+
+Upgrading a Flink Job always involves two steps: First, the Flink Job is gracefully stopped with a
+[Savepoint]({{ site.baseurl }}/ops/state/savepoints.html). A Savepoint is a consistent snapshot of
+the complete application state at a well-defined, globally consistent point in time (similar to a
+checkpoint). Second, the upgraded Flink Job is started from the Savepoint. In this context "upgrade"
+can mean different things including the following:
+
+* An upgrade to the configuration (incl. the parallelism of the Job)
+* An upgrade to the topology of the Job (added/removed Operators)
+* An upgrade to the user-defined functions of the Job
+
+Before starting with the upgrade you might want to start tailing the *output* topic, in order to
+observe that no data is lost or corrupted in the course the upgrade.
+
+{% highlight bash%}
+docker-compose exec kafka kafka-console-consumer.sh \
+ --bootstrap-server localhost:9092 --topic output
+{% endhighlight %}
+
+#### Step 1: Stopping the Job
+
+To gracefully stop the Job, you need to use the "stop" command of either the CLI or the REST API.
+For this you will need the JobID of the Job, which you can obtain by
+[listing all running Jobs](#listing-running-jobs) or from the WebUI. With the JobID you can proceed
+to stopping the Job:
+
+
+
+**Command**
+{% highlight bash %}
+docker-compose run --no-deps client flink stop
+{% endhighlight %}
+**Expected Output**
+{% highlight bash %}
+Suspending job "" with a savepoint.
+Suspended job "" with a savepoint.
+{% endhighlight %}
+
+The Savepoint has been stored to the `state.savepoint.dir` configured in the *flink-conf.yaml*,
+which is mounted under */tmp/flink-savepoints-directory/* on your local machine. You will need the
+path to this Savepoint in the next step. In case of the REST API this path was already part of the
+response, you will need to have a look at the filesystem directly.
+
+**Command**
+{% highlight bash %}
+ls -lia /tmp/flink-savepoints-directory
+{% endhighlight %}
+
+**Expected Output**
+{% highlight bash %}
+total 0
+ 17 drwxr-xr-x 3 root root 60 17 jul 17:05 .
+ 2 drwxrwxrwt 135 root root 3420 17 jul 17:09 ..
+1002 drwxr-xr-x 2 root root 140 17 jul 17:05 savepoint--
+{% endhighlight %}
+
+
+#### Step 2a: Restart Job without Changes
+
+You can now restart the upgraded Job from this Savepoint. For simplicity, you can start by
+restarting it without any changes.
+
+
+
+**Command**
+{% highlight bash %}
+docker-compose run --no-deps client flink run -s \
+ -d /opt/ClickCountJob.jar \
+ --bootstrap.servers kafka:9092 --checkpointing --event-time
+{% endhighlight %}
+**Expected Output**
+{% highlight bash %}
+Starting execution of program
+Job has been submitted with JobID
+{% endhighlight %}
+
+
+Once the Job is `RUNNING` again, you will see in the *output* Topic that records are produced at a
+higher rate while the Job is processing the backlog accumulated during the outage. Additionally,
+you will see that no data was lost during the upgrade: all windows are present with a count of
+exactly one thousand.
+
+#### Step 2b: Restart Job with a Different Parallelism (Rescaling)
+
+Alternatively, you could also rescale the Job from this Savepoint by passing a different parallelism
+during resubmission.
+
+
+
+**Command**
+{% highlight bash %}
+docker-compose run --no-deps client flink run -p 3 -s \
+ -d /opt/ClickCountJob.jar \
+ --bootstrap.servers kafka:9092 --checkpointing --event-time
+{% endhighlight %}
+**Expected Output**
+{% highlight bash %}
+Starting execution of program
+Job has been submitted with JobID
+{% endhighlight %}
+
+Now, the Job has been resubmitted, but it will not start as there are not enough TaskSlots to
+execute it with the increased parallelism (2 available, 3 needed). With
+{% highlight bash %}
+docker-compose scale taskmanager=2
+{% endhighlight %}
+you can add a second TaskManager with two TaskSlots to the Flink Cluster, which will automatically register with the
+JobManager. Shortly after adding the TaskManager the Job should start running again.
+
+Once the Job is "RUNNING" again, you will see in the *output* Topic that no data was lost during
+rescaling: all windows are present with a count of exactly one thousand.
+
+### Querying the Metrics of a Job
+
+The JobManager exposes system and user [metrics]({{ site.baseurl }}/monitoring/metrics.html)
+via its REST API.
+
+The endpoint depends on the scope of these metrics. Metrics scoped to a Job can be listed via
+`jobs//metrics`. The actual value of a metric can be queried via the `get` query parameter.
+
+**Request**
+{% highlight bash %}
+curl "localhost:8081/jobs//metrics?get=lastCheckpointSize"
+{% endhighlight %}
+**Expected Response (pretty-printed; no placeholders)**
+{% highlight json %}
+[
+ {
+ "id": "lastCheckpointSize",
+ "value": "9378"
+ }
+]
+{% endhighlight %}
+
+The REST API can not only be used to query metrics, but you can also retrieve detailed information
+about the status of a running Job.
+
+**Request**
+{% highlight bash %}
+# find the vertex-id of the vertex of interest
+curl localhost:8081/jobs/
+{% endhighlight %}
+
+**Expected Response (pretty-printed)**
+{% highlight json %}
+{
+ "jid": "",
+ "name": "Click Event Count",
+ "isStoppable": false,
+ "state": "RUNNING",
+ "start-time": 1564467066026,
+ "end-time": -1,
+ "duration": 374793,
+ "now": 1564467440819,
+ "timestamps": {
+ "CREATED": 1564467066026,
+ "FINISHED": 0,
+ "SUSPENDED": 0,
+ "FAILING": 0,
+ "CANCELLING": 0,
+ "CANCELED": 0,
+ "RECONCILING": 0,
+ "RUNNING": 1564467066126,
+ "FAILED": 0,
+ "RESTARTING": 0
+ },
+ "vertices": [
+ {
+ "id": "",
+ "name": "ClickEvent Source",
+ "parallelism": 2,
+ "status": "RUNNING",
+ "start-time": 1564467066423,
+ "end-time": -1,
+ "duration": 374396,
+ "tasks": {
+ "CREATED": 0,
+ "FINISHED": 0,
+ "DEPLOYING": 0,
+ "RUNNING": 2,
+ "CANCELING": 0,
+ "FAILED": 0,
+ "CANCELED": 0,
+ "RECONCILING": 0,
+ "SCHEDULED": 0
+ },
+ "metrics": {
+ "read-bytes": 0,
+ "read-bytes-complete": true,
+ "write-bytes": 5033461,
+ "write-bytes-complete": true,
+ "read-records": 0,
+ "read-records-complete": true,
+ "write-records": 166351,
+ "write-records-complete": true
+ }
+ },
+ {
+ "id": "",
+ "name": "Timestamps/Watermarks",
+ "parallelism": 2,
+ "status": "RUNNING",
+ "start-time": 1564467066441,
+ "end-time": -1,
+ "duration": 374378,
+ "tasks": {
+ "CREATED": 0,
+ "FINISHED": 0,
+ "DEPLOYING": 0,
+ "RUNNING": 2,
+ "CANCELING": 0,
+ "FAILED": 0,
+ "CANCELED": 0,
+ "RECONCILING": 0,
+ "SCHEDULED": 0
+ },
+ "metrics": {
+ "read-bytes": 5066280,
+ "read-bytes-complete": true,
+ "write-bytes": 5033496,
+ "write-bytes-complete": true,
+ "read-records": 166349,
+ "read-records-complete": true,
+ "write-records": 166349,
+ "write-records-complete": true
+ }
+ },
+ {
+ "id": "",
+ "name": "ClickEvent Counter",
+ "parallelism": 2,
+ "status": "RUNNING",
+ "start-time": 1564467066469,
+ "end-time": -1,
+ "duration": 374350,
+ "tasks": {
+ "CREATED": 0,
+ "FINISHED": 0,
+ "DEPLOYING": 0,
+ "RUNNING": 2,
+ "CANCELING": 0,
+ "FAILED": 0,
+ "CANCELED": 0,
+ "RECONCILING": 0,
+ "SCHEDULED": 0
+ },
+ "metrics": {
+ "read-bytes": 5085332,
+ "read-bytes-complete": true,
+ "write-bytes": 316,
+ "write-bytes-complete": true,
+ "read-records": 166305,
+ "read-records-complete": true,
+ "write-records": 6,
+ "write-records-complete": true
+ }
+ },
+ {
+ "id": "",
+ "name": "ClickEventStatistics Sink",
+ "parallelism": 2,
+ "status": "RUNNING",
+ "start-time": 1564467066476,
+ "end-time": -1,
+ "duration": 374343,
+ "tasks": {
+ "CREATED": 0,
+ "FINISHED": 0,
+ "DEPLOYING": 0,
+ "RUNNING": 2,
+ "CANCELING": 0,
+ "FAILED": 0,
+ "CANCELED": 0,
+ "RECONCILING": 0,
+ "SCHEDULED": 0
+ },
+ "metrics": {
+ "read-bytes": 20668,
+ "read-bytes-complete": true,
+ "write-bytes": 0,
+ "write-bytes-complete": true,
+ "read-records": 6,
+ "read-records-complete": true,
+ "write-records": 0,
+ "write-records-complete": true
+ }
+ }
+ ],
+ "status-counts": {
+ "CREATED": 0,
+ "FINISHED": 0,
+ "DEPLOYING": 0,
+ "RUNNING": 4,
+ "CANCELING": 0,
+ "FAILED": 0,
+ "CANCELED": 0,
+ "RECONCILING": 0,
+ "SCHEDULED": 0
+ },
+ "plan": {
+ "jid": "",
+ "name": "Click Event Count",
+ "nodes": [
+ {
+ "id": "",
+ "parallelism": 2,
+ "operator": "",
+ "operator_strategy": "",
+ "description": "ClickEventStatistics Sink",
+ "inputs": [
+ {
+ "num": 0,
+ "id": "",
+ "ship_strategy": "FORWARD",
+ "exchange": "pipelined_bounded"
+ }
+ ],
+ "optimizer_properties": {}
+ },
+ {
+ "id": "",
+ "parallelism": 2,
+ "operator": "",
+ "operator_strategy": "",
+ "description": "ClickEvent Counter",
+ "inputs": [
+ {
+ "num": 0,
+ "id": "",
+ "ship_strategy": "HASH",
+ "exchange": "pipelined_bounded"
+ }
+ ],
+ "optimizer_properties": {}
+ },
+ {
+ "id": "",
+ "parallelism": 2,
+ "operator": "",
+ "operator_strategy": "",
+ "description": "Timestamps/Watermarks",
+ "inputs": [
+ {
+ "num": 0,
+ "id": "",
+ "ship_strategy": "FORWARD",
+ "exchange": "pipelined_bounded"
+ }
+ ],
+ "optimizer_properties": {}
+ },
+ {
+ "id": "",
+ "parallelism": 2,
+ "operator": "",
+ "operator_strategy": "",
+ "description": "ClickEvent Source",
+ "optimizer_properties": {}
+ }
+ ]
+ }
+}
+{% endhighlight %}
+
+Please consult the [REST API reference]({{ site.baseurl }}/monitoring/rest_api.html#api)
+for a complete list of possible queries including how to query metrics of different scopes (e.g.
+TaskManager metrics);
+
+{% top %}
+
+## Variants
+
+You might have noticed that the *Click Event Count* application was always started with `--checkpointing`
+and `--event-time` program arguments. By omitting these in the command of the *client* container in the
+`docker-compose.yaml`, you can change the behavior of the Job.
+
+* `--checkpointing` enables [checkpoint]({{ site.baseurl }}/learn-flink/fault_tolerance.html),
+which is Flink's fault-tolerance mechanism. If you run without it and go through
+[failure and recovery](#observing-failure--recovery), you should will see that data is actually
+lost.
+
+* `--event-time` enables [event time semantics]({{ site.baseurl }}/dev/event_time.html) for your
+Job. When disabled, the Job will assign events to windows based on the wall-clock time instead of
+the timestamp of the `ClickEvent`. Consequently, the number of events per window will not be exactly
+one thousand anymore.
+
+The *Click Event Count* application also has another option, turned off by default, that you can
+enable to explore the behavior of this job under backpressure. You can add this option in the
+command of the *client* container in `docker-compose.yaml`.
+
+* `--backpressure` adds an additional operator into the middle of the job that causes severe backpressure
+during even-numbered minutes (e.g., during 10:12, but not during 10:13). This can be observed by
+inspecting various [network metrics]({{ site.baseurl }}/monitoring/metrics.html#default-shuffle-service)
+such as `outputQueueLength` and `outPoolUsage`, and/or by using the
+[backpressure monitoring]({{ site.baseurl }}/monitoring/back_pressure.html#monitoring-back-pressure)
+available in the WebUI.
diff --git a/docs/getting-started/docker-playgrounds/flink-operations-playground.zh.md b/docs/try-flink/flink-operations-playground.zh.md
similarity index 96%
rename from docs/getting-started/docker-playgrounds/flink-operations-playground.zh.md
rename to docs/try-flink/flink-operations-playground.zh.md
index 1e2a569051f2bf..0945a2f85518eb 100644
--- a/docs/getting-started/docker-playgrounds/flink-operations-playground.zh.md
+++ b/docs/try-flink/flink-operations-playground.zh.md
@@ -1,8 +1,8 @@
---
-title: "Flink Operations Playground"
-nav-title: 'Flink Operations Playground'
-nav-parent_id: docker-playgrounds
-nav-pos: 1
+title: "Flink 运维操场"
+nav-title: 'Flink 运维操场'
+nav-parent_id: try-flink
+nav-pos: 5
---
+
diff --git a/docs/try-flink/index.zh.md b/docs/try-flink/index.zh.md
new file mode 100644
index 00000000000000..90f3a1d8acf5a5
--- /dev/null
+++ b/docs/try-flink/index.zh.md
@@ -0,0 +1,26 @@
+---
+title: "Try Flink"
+nav-id: try-flink
+nav-title: ' Try Flink'
+nav-parent_id: root
+section-break: true
+nav-pos: 1
+---
+
diff --git a/docs/try-flink/local_installation.md b/docs/try-flink/local_installation.md
new file mode 100644
index 00000000000000..e6e5b50ba30401
--- /dev/null
+++ b/docs/try-flink/local_installation.md
@@ -0,0 +1,92 @@
+---
+title: "Local Installation"
+nav-title: 'Local Installation'
+nav-parent_id: try-flink
+nav-pos: 1
+---
+
+
+{% if site.version contains "SNAPSHOT" %}
+
+
+ NOTE: The Apache Flink community only publishes official builds for
+ released versions of Apache Flink.
+
+ Since you are currently looking at the latest SNAPSHOT
+ version of the documentation, all version references below will not work.
+ Please switch the documentation to the latest released version via the release picker which you
+ find on the left side below the menu.
+
+{% else %}
+Follow these few steps to download the latest stable versions and get started.
+
+## Step 1: Download
+
+To be able to run Flink, the only requirement is to have a working __Java 8 or 11__ installation.
+You can check the correct installation of Java by issuing the following command:
+
+{% highlight bash %}
+java -version
+{% endhighlight %}
+
+[Download](https://flink.apache.org/downloads.html) the {{ site.version }} release and un-tar it.
+
+{% highlight bash %}
+$ tar -xzf flink-{{ site.version }}-bin-scala{{ site.scala_version_suffix }}.tgz
+$ cd flink-{{ site.version }}-bin-scala{{ site.scala_version_suffix }}
+{% endhighlight %}
+
+## Step 2: Start a Cluster
+
+Flink ships with a single bash script to start a local cluster.
+
+{% highlight bash %}
+$ ./bin/start-cluster.sh
+Starting cluster.
+Starting standalonesession daemon on host.
+Starting taskexecutor daemon on host.
+{% endhighlight %}
+
+## Step 3: Submit a Job
+
+Releases of Flink come with a number of example Jobs.
+You can quickly deploy one of these applications to the running cluster.
+
+{% highlight bash %}
+$ ./bin/flink run examples/streaming/WordCount.jar
+$ tail log/flink-*-taskexecutor-*.out
+ (to,1)
+ (be,1)
+ (or,1)
+ (not,1)
+ (to,2)
+ (be,2)
+{% endhighlight %}
+
+Additionally, you can check Flink's [Web UI](http://localhost:8080) to monitor the status of the Cluster and running Job.
+
+## Step 4: Stop the Cluster
+
+When you are finished you can quickly stop the cluster and all running components.
+
+{% highlight bash %}
+$ ./bin/stop-cluster.sh
+{% endhighlight %}
+{% endif %}
diff --git a/docs/try-flink/local_installation.zh.md b/docs/try-flink/local_installation.zh.md
new file mode 100644
index 00000000000000..e6e5b50ba30401
--- /dev/null
+++ b/docs/try-flink/local_installation.zh.md
@@ -0,0 +1,92 @@
+---
+title: "Local Installation"
+nav-title: 'Local Installation'
+nav-parent_id: try-flink
+nav-pos: 1
+---
+
+
+{% if site.version contains "SNAPSHOT" %}
+
+
+ NOTE: The Apache Flink community only publishes official builds for
+ released versions of Apache Flink.
+
+ Since you are currently looking at the latest SNAPSHOT
+ version of the documentation, all version references below will not work.
+ Please switch the documentation to the latest released version via the release picker which you
+ find on the left side below the menu.
+
+{% else %}
+Follow these few steps to download the latest stable versions and get started.
+
+## Step 1: Download
+
+To be able to run Flink, the only requirement is to have a working __Java 8 or 11__ installation.
+You can check the correct installation of Java by issuing the following command:
+
+{% highlight bash %}
+java -version
+{% endhighlight %}
+
+[Download](https://flink.apache.org/downloads.html) the {{ site.version }} release and un-tar it.
+
+{% highlight bash %}
+$ tar -xzf flink-{{ site.version }}-bin-scala{{ site.scala_version_suffix }}.tgz
+$ cd flink-{{ site.version }}-bin-scala{{ site.scala_version_suffix }}
+{% endhighlight %}
+
+## Step 2: Start a Cluster
+
+Flink ships with a single bash script to start a local cluster.
+
+{% highlight bash %}
+$ ./bin/start-cluster.sh
+Starting cluster.
+Starting standalonesession daemon on host.
+Starting taskexecutor daemon on host.
+{% endhighlight %}
+
+## Step 3: Submit a Job
+
+Releases of Flink come with a number of example Jobs.
+You can quickly deploy one of these applications to the running cluster.
+
+{% highlight bash %}
+$ ./bin/flink run examples/streaming/WordCount.jar
+$ tail log/flink-*-taskexecutor-*.out
+ (to,1)
+ (be,1)
+ (or,1)
+ (not,1)
+ (to,2)
+ (be,2)
+{% endhighlight %}
+
+Additionally, you can check Flink's [Web UI](http://localhost:8080) to monitor the status of the Cluster and running Job.
+
+## Step 4: Stop the Cluster
+
+When you are finished you can quickly stop the cluster and all running components.
+
+{% highlight bash %}
+$ ./bin/stop-cluster.sh
+{% endhighlight %}
+{% endif %}
diff --git a/docs/getting-started/walkthroughs/python_table_api.md b/docs/try-flink/python_table_api.md
similarity index 99%
rename from docs/getting-started/walkthroughs/python_table_api.md
rename to docs/try-flink/python_table_api.md
index 553289d54361d7..c4629fafe4133d 100644
--- a/docs/getting-started/walkthroughs/python_table_api.md
+++ b/docs/try-flink/python_table_api.md
@@ -1,8 +1,8 @@
---
title: "Python API Tutorial"
nav-title: Python API
-nav-parent_id: walkthroughs
-nav-pos: 10
+nav-parent_id: try-flink
+nav-pos: 4
---
+
+Apache Flink offers a Table API as a unified, relational API for batch and stream processing, i.e., queries are executed with the same semantics on unbounded, real-time streams or bounded, batch data sets and produce the same results.
+The Table API in Flink is commonly used to ease the definition of data analytics, data pipelining, and ETL applications.
+
+* This will be replaced by the TOC
+{:toc}
+
+## What Will You Be Building?
+
+In this tutorial, you will learn how to build a real-time dashboard to track financial transactions by account.
+The pipeline will read data from Kafka and write the results to MySQL visualized via Grafana.
+
+## Prerequisites
+
+This walkthrough assumes that you have some familiarity with Java or Scala, but you should be able to follow along even if you come from a different programming language.
+It also assumes that you are familiar with basic relational concepts such as `SELECT` and `GROUP BY` clauses.
+
+## Help, I’m Stuck!
+
+If you get stuck, check out the [community support resources](https://flink.apache.org/community.html).
+In particular, Apache Flink's [user mailing list](https://flink.apache.org/community.html#mailing-lists) consistently ranks as one of the most active of any Apache project and a great way to get help quickly.
+
+## How To Follow Along
+
+If you want to follow along, you will require a computer with:
+
+* Java 8 or 11
+* Maven
+* Docker
+
+{% if site.version contains "SNAPSHOT" %}
+
+
+ NOTE: The Apache Flink Docker images used for this playground are only available for
+ released versions of Apache Flink.
+
+ Since you are currently looking at the latest SNAPSHOT
+ version of the documentation, all version references below will not work.
+ Please switch the documentation to the latest released version via the release picker which you
+ find on the left side below the menu.
+
+{% endif %}
+
+The required configuration files are available in the [flink-playgrounds](https://github.com/apache/flink-playgrounds) repository.
+Once downloaded, open the project `flink-playground/table-walkthrough` in your IDE and navigate to the file `SpendReport`.
+
+{% highlight java %}
+EnvironmentSettings settings = EnvironmentSettings.newInstance().build();
+TableEnvironment tEnv = TableEnvironment.create(settings);
+
+tEnv.executeSql("CREATE TABLE transactions (\n" +
+ " account_id BIGINT,\n" +
+ " amount BIGINT,\n" +
+ " transaction_time TIMESTAMP(3),\n" +
+ " WATERMARK FOR transaction_time AS transaction_time - INTERVAL '5' SECOND\n" +
+ ") WITH (\n" +
+ " 'connector' = 'kafka',\n" +
+ " 'topic' = 'transactions',\n" +
+ " 'properties.bootstrap.servers' = 'kafka:9092',\n" +
+ " 'format' = 'csv'\n" +
+ ")");
+
+tEnv.executeSql("CREATE TABLE spend_report (\n" +
+ " account_id BIGINT,\n" +
+ " log_ts TIMESTAMP(3),\n" +
+ " amount BIGINT\n," +
+ " PRIMARY KEY (account_id, log_ts) NOT ENFORCED" +
+ ") WITH (\n" +
+ " 'connector' = 'jdbc',\n" +
+ " 'url' = 'jdbc:mysql://mysql:3306/sql-demo',\n" +
+ " 'table-name' = 'spend_report',\n" +
+ " 'driver' = 'com.mysql.jdbc.Driver',\n" +
+ " 'username' = 'sql-demo',\n" +
+ " 'password' = 'demo-sql'\n" +
+ ")");
+
+Table transactions = tEnv.from("transactions");
+report(transactions).executeInsert("spend_report");
+
+{% endhighlight %}
+
+## Breaking Down The Code
+
+#### The Execution Environment
+
+The first two lines set up your `TableEnvironment`.
+The table environment is how you can set properties for your Job, specify whether you are writing a batch or a streaming application, and create your sources.
+This walkthrough creates a standard table environment that uses the streaming execution.
+
+{% highlight java %}
+EnvironmentSettings settings = EnvironmentSettings.newInstance().build();
+TableEnvironment tEnv = TableEnvironment.create(settings);
+{% endhighlight %}
+
+#### Registering Tables
+
+Next, tables are registered in the current [catalog]({% link dev/table/catalogs.md %}) that you can use to connect to external systems for reading and writing both batch and streaming data.
+A table source provides access to data stored in external systems, such as a database, a key-value store, a message queue, or a file system.
+A table sink emits a table to an external storage system.
+Depending on the type of source and sink, they support different formats such as CSV, JSON, Avro, or Parquet.
+
+{% highlight java %}
+tEnv.executeSql("CREATE TABLE transactions (\n" +
+ " account_id BIGINT,\n" +
+ " amount BIGINT,\n" +
+ " transaction_time TIMESTAMP(3),\n" +
+ " WATERMARK FOR transaction_time AS transaction_time - INTERVAL '5' SECOND\n" +
+ ") WITH (\n" +
+ " 'connector' = 'kafka',\n" +
+ " 'topic' = 'transactions',\n" +
+ " 'properties.bootstrap.servers' = 'kafka:9092',\n" +
+ " 'format' = 'csv'\n" +
+ ")");
+{% endhighlight %}
+
+Two tables are registered; a transaction input table, and a spend report output table.
+The transactions (`transactions`) table lets us read credit card transactions, which contain account ID's (`account_id`), timestamps (`transaction_time`), and US$ amounts (`amount`).
+The table is a logical view over a Kafka topic called `transactions` containing CSV data.
+
+{% highlight java %}
+tEnv.executeSql("CREATE TABLE spend_report (\n" +
+ " account_id BIGINT,\n" +
+ " log_ts TIMESTAMP(3),\n" +
+ " amount BIGINT\n," +
+ " PRIMARY KEY (account_id, log_ts) NOT ENFORCED" +
+ ") WITH (\n" +
+ " 'connector' = 'jdbc',\n" +
+ " 'url' = 'jdbc:mysql://mysql:3306/sql-demo',\n" +
+ " 'table-name' = 'spend_report',\n" +
+ " 'driver' = 'com.mysql.jdbc.Driver',\n" +
+ " 'username' = 'sql-demo',\n" +
+ " 'password' = 'demo-sql'\n" +
+ ")");
+{% endhighlight %}
+
+The second table, `spend_report`, stores the final results of the aggregation.
+Its underlying storage is a table in a MySql database.
+
+#### The Query
+
+With the environment configured and tables registered, you are ready to build your first application.
+From the `TableEnvironment` you can read `from` an input table to read its rows and then write those results into an output table using `executeInsert`.
+The `report` function is where you will implement your business logic.
+It is currently unimplemented.
+
+{% highlight java %}
+Table transactions = tEnv.from("transactions");
+report(transactions).executeInsert("spend_report");
+{% endhighlight %}
+
+## Testing
+
+The project contains a secondary testing class `SpendReportTest` that validates the logic of the report.
+It creates a table environment in batch mode.
+
+{% highlight java %}
+EnvironmentSettings settings = EnvironmentSettings.newInstance().inBatchMode().build();
+TableEnvironment tEnv = TableEnvironment.create(settings);
+{% endhighlight %}
+
+One of Flink's unique properties is that it provides consistent semantics across batch and streaming.
+This means you can develop and test applications in batch mode on static datasets, and deploy to production as streaming applications.
+
+## Attempt One
+
+Now with the skeleton of a Job set-up, you are ready to add some business logic.
+The goal is to build a report that shows the total spend for each account across each hour of the day.
+This means the timestamp column needs be be rounded down from millisecond to hour granularity.
+
+Flink supports developing relational applications in pure [SQL]({% link dev/table/sql/index.md %}) or using the [Table API]({% link dev/table/tableApi.md %}).
+The Table API is a fluent DSL inspired by SQL, that can be written in Python, Java, or Scala and supports strong IDE integration.
+Just like a SQL query, Table programs can select the required fields and group by your keys.
+These features, allong with [built-in functions]({% link dev/table/functions/systemFunctions.md %}) like `floor` and `sum`, you can write this report.
+
+{% highlight java %}
+public static Table report(Table transactions) {
+ return transactions.select(
+ $("account_id"),
+ $("transaction_time").floor(TimeIntervalUnit.HOUR).as("log_ts"),
+ $("amount"))
+ .groupBy($("account_id"), $("log_ts"))
+ .select(
+ $("account_id"),
+ $("log_ts"),
+ $("amount").sum().as("amount"));
+}
+{% endhighlight %}
+
+## User Defined Functions
+
+Flink contains a limited number of built-in functions, and sometimes you need to extend it with a [user-defined function]({% link dev/table/functions/udfs.md %}).
+If `floor` wasn't predefined, you could implement it yourself.
+
+{% highlight java %}
+import java.time.LocalDateTime;
+import java.time.temporal.ChronoUnit;
+
+import org.apache.flink.table.annotation.DataTypeHint;
+import org.apache.flink.table.functions.ScalarFunction;
+
+public class MyFloor extends ScalarFunction {
+
+ public @DataTypeHint("TIMESTAMP(3)") LocalDateTime eval(
+ @DataTypeHint("TIMESTAMP(3)") LocalDateTime timestamp) {
+
+ return timestamp.truncatedTo(ChronoUnit.HOURS);
+ }
+}
+{% endhighlight %}
+
+And then quickly integrate it in your application.
+
+{% highlight java %}
+public static Table report(Table transactions) {
+ return transactions.select(
+ $("account_id"),
+ call(MyFloor.class, $("transaction_time")).as("log_ts"),
+ $("amount"))
+ .groupBy($("account_id"), $("log_ts"))
+ .select(
+ $("account_id"),
+ $("log_ts"),
+ $("amount").sum().as("amount"));
+}
+{% endhighlight %}
+
+This query consumes all records from the `transactions` table, calculates the report, and outputs the results in an efficient, scalable manner.
+Running the test with this implementation will pass.
+
+## Adding Windows
+
+Grouping data based on time is a typical operation in data processing, especially when working with infinite streams.
+A grouping based on time is called a [window]({% link dev/stream/operators/windows.md %}) and Flink offers flexible windowing semantics.
+The most basic type of window is called a `Tumble` window, which has a fixed size and whose buckets do not overlap.
+
+{% highlight java %}
+public static Table report(Table transactions) {
+ return transactions
+ .window(Tumble.over(lit(1).hour()).on($("transaction_time")).as("log_ts"))
+ .groupBy($("account_id"), $("log_ts"))
+ .select(
+ $("account_id"),
+ $("log_ts").start().as("log_ts"),
+ $("amount").sum().as("amount"));
+}
+{% endhighlight %}
+
+This defines your application as using one hour tumbling windows based on the timestamp column.
+So a row with timestamp `2019-06-01 01:23:47` is put in the `2019-06-01 01:00:00` window.
+
+
+Aggregations based on time are unique because time, as opposed to other attributes, generally moves forward in a continuous streaming application.
+Unlike `floor` and your UDF, window functions are [intrinsics](https://en.wikipedia.org/wiki/Intrinsic_function), which allows the runtime to apply additional optimizations.
+In a batch context, windows offer a convenient API for grouping records by a timestamp attribute.
+
+Running the test with this implementation will also pass.
+
+## Once More, With Streaming!
+
+And that's it, a fully functional, stateful, distributed streaming application!
+The query continuously consumes the stream of transactions from Kafka, computes the hourly spendings, and emits results as soon as they are ready.
+Since the input is unbounded, the query keeps running until it is manually stopped.
+And because the Job uses time window-based aggregations, Flink can perform specific optimizations such as state clean up when the framework knows that no more records will arrive for a particular window.
+
+The table playground is fully dockerized and runnable locally as streaming application.
+The environment contains a Kafka topic, a continuous data generator, MySql, and Grafana.
+
+From within the `table-walkthrough` folder start the docker-compose script.
+
+{% highlight bash %}
+$ docker-compose build
+$ docker-compose up -d
+{% endhighlight %}
+
+You can see information on the running job via the [Flink console](http://localhost:8082/).
+
+{:height="400px" width="800px"}
+
+Explore the results from inside MySQL.
+
+{% highlight bash %}
+$ docker-compose exec mysql mysql -Dsql-demo -usql-demo -pdemo-sql
+
+mysql> use sql-demo;
+Database changed
+
+mysql> select count(*) from spend_report;
++----------+
+| count(*) |
++----------+
+| 110 |
++----------+
+{% endhighlight %}
+
+Finally, go to [Grafana](http://localhost:3000/d/FOe0PbmGk/walkthrough?viewPanel=2&orgId=1&refresh=5s) to see the fully visualized result!
+
+{:height="400px" width="800px"}
diff --git a/docs/try-flink/table_api.zh.md b/docs/try-flink/table_api.zh.md
new file mode 100644
index 00000000000000..339bb80f48e8bb
--- /dev/null
+++ b/docs/try-flink/table_api.zh.md
@@ -0,0 +1,320 @@
+---
+title: "基于 Table API 实现实时报表"
+nav-title: '基于 Table API 实现实时报表'
+nav-parent_id: try-flink
+nav-pos: 3
+---
+
+
+Apache Flink offers a Table API as a unified, relational API for batch and stream processing, i.e., queries are executed with the same semantics on unbounded, real-time streams or bounded, batch data sets and produce the same results.
+The Table API in Flink is commonly used to ease the definition of data analytics, data pipelining, and ETL applications.
+
+* This will be replaced by the TOC
+{:toc}
+
+## What Will You Be Building?
+
+In this tutorial, you will learn how to build a real-time dashboard to track financial transactions by account.
+The pipeline will read data from Kafka and write the results to MySQL visualized via Grafana.
+
+## Prerequisites
+
+This walk-through assumes that you have some familiarity with Java or Scala, but you should be able to follow along even if you come from a different programming language.
+It also assumes that you are familiar with basic relational concepts such as `SELECT` and `GROUP BY` clauses.
+
+## Help, I’m Stuck!
+
+If you get stuck, check out the [community support resources](https://flink.apache.org/community.html).
+In particular, Apache Flink's [user mailing list](https://flink.apache.org/community.html#mailing-lists) consistently ranks as one of the most active of any Apache project and a great way to get help quickly.
+
+## How To Follow Along
+
+If you want to follow along, you will require a computer with:
+
+* Java 8 or 11
+* Maven
+* Docker
+
+{% if site.version contains "SNAPSHOT" %}
+
+
+ NOTE: The Apache Flink Docker images used for this playground are only available for
+ released versions of Apache Flink.
+
+ Since you are currently looking at the latest SNAPSHOT
+ version of the documentation, all version references below will not work.
+ Please switch the documentation to the latest released version via the release picker which you
+ find on the left side below the menu.
+
+{% endif %}
+
+The required configuration files are available in the [flink-playgrounds](https://github.com/apache/flink-playgrounds) repository.
+Once downloaded, open the project `flink-playground/table-walkthrough` in your IDE and navigate to the file `SpendReport`.
+
+{% highlight java %}
+EnvironmentSettings settings = EnvironmentSettings.newInstance().build();
+TableEnvironment tEnv = TableEnvironment.create(settings);
+
+tEnv.executeSql("CREATE TABLE transactions (\n" +
+ " account_id BIGINT,\n" +
+ " amount BIGINT,\n" +
+ " transaction_time TIMESTAMP(3),\n" +
+ " WATERMARK FOR transaction_time AS transaction_time - INTERVAL '5' SECOND\n" +
+ ") WITH (\n" +
+ " 'connector' = 'kafka',\n" +
+ " 'topic' = 'transactions',\n" +
+ " 'properties.bootstrap.servers' = 'kafka:9092',\n" +
+ " 'format' = 'csv'\n" +
+ ")");
+
+tEnv.executeSql("CREATE TABLE spend_report (\n" +
+ " account_id BIGINT,\n" +
+ " log_ts TIMESTAMP(3),\n" +
+ " amount BIGINT\n," +
+ " PRIMARY KEY (account_id, log_ts) NOT ENFORCED" +
+ ") WITH (\n" +
+ " 'connector' = 'jdbc',\n" +
+ " 'url' = 'jdbc:mysql://mysql:3306/sql-demo',\n" +
+ " 'table-name' = 'spend_report',\n" +
+ " 'driver' = 'com.mysql.jdbc.Driver',\n" +
+ " 'username' = 'sql-demo',\n" +
+ " 'password' = 'demo-sql'\n" +
+ ")");
+
+Table transactions = tEnv.from("transactions");
+report(transactions).executeInsert("spend_report");
+
+{% endhighlight %}
+
+## Breaking Down The Code
+
+#### The Execution Environment
+
+The first two lines set up your `TableEnvironment`.
+The table environment is how you can set properties for your Job, specify whether you are writing a batch or a streaming application, and create your sources.
+This walkthrough creates a standard table environment that uses the streaming runtime.
+
+{% highlight java %}
+EnvironmentSettings settings = EnvironmentSettings.newInstance().build();
+TableEnvironment tEnv = TableEnvironment.create(settings);
+{% endhighlight %}
+
+#### Registering Tables
+
+Next, tables are registered in the environment that you can use to connect to external systems for reading and writing both batch and streaming data.
+A table source provides access to data stored in external systems, such as a database, a key-value store, a message queue, or a file system.
+A table sink emits a table to an external storage system.
+Depending on the type of source and sink, they support different formats such as CSV, JSON, Avro, or Parquet.
+
+{% highlight sql %}
+tEnv.executeSql("CREATE TABLE transactions (\n" +
+ " account_id BIGINT,\n" +
+ " amount BIGINT,\n" +
+ " transaction_time TIMESTAMP(3),\n" +
+ " WATERMARK FOR transaction_time AS transaction_time - INTERVAL '5' SECOND\n" +
+ ") WITH (\n" +
+ " 'connector' = 'kafka',\n" +
+ " 'topic' = 'transactions',\n" +
+ " 'properties.bootstrap.servers' = 'kafka:9092',\n" +
+ " 'format' = 'csv'\n" +
+ ")");
+{% endhighlight %}
+
+Two tables are registered; a transaction input table, and a spend report output table.
+The transactions (`transactions`) table lets us read credit card transactions, which contain account ID's (`account_id`), timestamps (`transaction_time`), and US$ amounts (`amount`).
+The table is a logical view over a Kafka topic called `transactions` containing CSV data.
+
+{% highlight sql %}
+tEnv.executeSql("CREATE TABLE spend_report (\n" +
+ " account_id BIGINT,\n" +
+ " log_ts TIMESTAMP(3),\n" +
+ " amount BIGINT\n," +
+ " PRIMARY KEY (account_id, log_ts) NOT ENFORCED" +
+ ") WITH (\n" +
+ " 'connector' = 'jdbc',\n" +
+ " 'url' = 'jdbc:mysql://mysql:3306/sql-demo',\n" +
+ " 'table-name' = 'spend_report',\n" +
+ " 'driver' = 'com.mysql.jdbc.Driver',\n" +
+ " 'username' = 'sql-demo',\n" +
+ " 'password' = 'demo-sql'\n" +
+ ")");
+{% endhighlight %}
+
+The second table, `spend_report`, stores the final results of the aggregation.
+Its underlying storage is a table in a MySql database.
+
+#### The Query
+
+With the environment configured and tables registered, you are ready to build your first application.
+From the `TableEnvironment` you can read `from` an input table to read its rows and then write those results into an output table using `executeInsert`.
+The `report` function is where you will implement your business logic.
+It is currently unimplemented.
+
+{% highlight java %}
+Table transactions = tEnv.from("transactions");
+report(transactions).executeInsert("spend_report");
+{% endhighlight %}
+
+## Testing
+
+The project contains a secondary testing class `SpendReportTest` that validates the logic of the report.
+It creates a table environment in batch mode.
+
+{% highlight java %}
+EnvironmentSettings settings = EnvironmentSettings.newInstance().inBatchMode().build();
+TableEnvironment tEnv = TableEnvironment.create(settings);
+{% endhighlight %}
+
+One of Flink's unique properties is that it provides consistent semantics across batch and streaming.
+This means you can develop and test applications in batch mode on static datasets, and deploy to production as streaming applications!
+
+## Attempt One
+
+Now with the skeleton of a Job set-up, you are ready to add some business logic.
+The goal is to build a report that shows the total spend for each account across each hour of the day.
+This means the timestamp column needs be be rounded down from millisecond to hour granularity.
+
+Just like a SQL query, Flink can select the required fields and group by your keys.
+These features, allong with [built-in functions]({% link dev/table/functions/systemFunctions.zh.md %}) like `floor` and `sum`, you can write this report.
+
+{% highlight java %}
+public static Table report(Table rows) {
+ return rows.select(
+ $("account_id"),
+ $("transaction_time").floor(TimeIntervalUnit.HOUR).as("log_ts"),
+ $("amount"))
+ .groupBy($("account_id"), $("log_ts"))
+ .select(
+ $("account_id"),
+ $("log_ts"),
+ $("amount").sum().as("amount"));
+}
+{% endhighlight %}
+
+## User Defined Functions
+
+Flink contains a limited number of built-in functions, and sometimes you need to extend it with a [user-defined function]({% link dev/table/functions/udfs.zh.md %}).
+If `floor` wasn't predefined, you could implement it yourself.
+
+{% highlight java %}
+import java.time.LocalDateTime;
+import java.time.temporal.ChronoUnit;
+
+import org.apache.flink.table.annotation.DataTypeHint;
+import org.apache.flink.table.functions.ScalarFunction;
+
+public class MyFloor extends ScalarFunction {
+
+ public @DataTypeHint("TIMESTAMP(3)") LocalDateTime eval(
+ @DataTypeHint("TIMESTAMP(3)") LocalDateTime timestamp) {
+
+ return timestamp.truncatedTo(ChronoUnit.HOURS);
+ }
+}
+{% endhighlight %}
+
+And then quickly integrate it in your application.
+
+{% highlight java %}
+public static Table report(Table rows) {
+ return rows.select(
+ $("account_id"),
+ call(MyFloor.class, $("transaction_time")).as("log_ts"),
+ $("amount"))
+ .groupBy($("account_id"), $("log_ts"))
+ .select(
+ $("account_id"),
+ $("log_ts"),
+ $("amount").sum().as("amount"));
+}
+{% endhighlight %}
+
+This query consumes all records from the `transactions` table, calculates the report, and outputs the results in an efficient, scalable manner.
+Running the test with this implementation will pass.
+
+## Adding Windows
+
+Grouping data based on time is a typical operation in data processing, especially when working with infinite streams.
+A grouping based on time is called a [window]({% link dev/stream/operators/windows.zh.md %}) and Flink offers flexible windowing semantics.
+The most basic type of window is called a `Tumble` window, which has a fixed size and whose buckets do not overlap.
+
+{% highlight java %}
+public static Table report(Table rows) {
+ return rows.window(Tumble.over(lit(1).hour()).on($("transaction_time")).as("log_ts"))
+ .groupBy($("account_id"), $("log_ts"))
+ .select(
+ $("account_id"),
+ $("log_ts").start().as("log_ts"),
+ $("amount").sum().as("amount"));
+}
+{% endhighlight %}
+
+This defines your application as using one hour tumbling windows based on the timestamp column.
+So a row with timestamp `2019-06-01 01:23:47` is put in the `2019-06-01 01:00:00` window.
+
+
+Aggregations based on time are unique because time, as opposed to other attributes, generally moves forward in a continuous streaming application.
+Unlike `floor` and your UDF, window functions are [intrinsics](https://en.wikipedia.org/wiki/Intrinsic_function), which allows the runtime to apply additional optimizations.
+In a batch context, windows offer a convenient API for grouping records by a timestamp attribute.
+
+Running the test with this implementation will also pass.
+
+## Once More, With Streaming!
+
+And that's it, a fully functional, stateful, distributed streaming application!
+The query continuously consumes the stream of transactions from Kafka, computes the hourly spendings, and emits results as soon as they are ready.
+Since the input is unbounded, the query keeps running until it is manually stopped.
+And because the Job uses time window-based aggregations, Flink can perform specific optimizations such as state clean up when the framework knows that no more records will arrive for a particular window.
+
+The table playground is fully dockerized and runnable locally as streaming application.
+The environment contains a Kafka topic, a continuous data generator, MySql, and Grafana.
+
+From within the `table-walkthrough` folder start the docker-compose script.
+
+{% highlight bash %}
+$ docker-compose build
+$ docker-compose up -d
+{% endhighlight %}
+
+You can see information on the running job via the [Flink console](http://localhost:8082/).
+
+{:height="400px" width="800px"}
+
+Explore the results from inside MySQL.
+
+{% highlight bash %}
+$ docker-compose exec mysql mysql -Dsql-demo -usql-demo -pdemo-sql
+
+mysql> use sql-demo;
+Database changed
+
+mysql> select count(*) from spend_report;
++----------+
+| count(*) |
++----------+
+| 110 |
++----------+
+{% endhighlight %}
+
+Finally, go to [Grafana](http://localhost:3000/d/FOe0PbmGk/walkthrough?viewPanel=2&orgId=1&refresh=5s) to see the fully visualized result!
+
+{:height="400px" width="800px"}
+
+
diff --git a/flink-clients/pom.xml b/flink-clients/pom.xml
index 6505844814798d..65a55fa70b95db 100644
--- a/flink-clients/pom.xml
+++ b/flink-clients/pom.xml
@@ -88,6 +88,14 @@ under the License.
flink-streaming-java_${scala.binary.version}${project.version}
+
+
+ org.apache.flink
+ flink-core
+ ${project.version}
+ test
+ test-jar
+
+
+ org.apache.flink
+ flink-core
+ ${project.version}
+ test
+ test-jar
+
+
org.apache.flinkflink-clients_${scala.binary.version}
diff --git a/flink-connectors/flink-connector-hbase/src/main/java/org/apache/flink/addons/hbase/TableInputFormat.java b/flink-connectors/flink-connector-hbase/src/main/java/org/apache/flink/addons/hbase/TableInputFormat.java
index 9be258e5245fc6..a5e044e02767ec 100644
--- a/flink-connectors/flink-connector-hbase/src/main/java/org/apache/flink/addons/hbase/TableInputFormat.java
+++ b/flink-connectors/flink-connector-hbase/src/main/java/org/apache/flink/addons/hbase/TableInputFormat.java
@@ -31,4 +31,8 @@
public abstract class TableInputFormat extends HBaseInputFormat {
private static final long serialVersionUID = 1L;
+ public TableInputFormat(org.apache.hadoop.conf.Configuration hConf) {
+ super(hConf);
+ }
+
}
diff --git a/flink-connectors/flink-connector-hbase/src/main/java/org/apache/flink/connector/hbase/HBaseDynamicTableFactory.java b/flink-connectors/flink-connector-hbase/src/main/java/org/apache/flink/connector/hbase/HBaseDynamicTableFactory.java
index ba855772a0535e..5db9968972318b 100644
--- a/flink-connectors/flink-connector-hbase/src/main/java/org/apache/flink/connector/hbase/HBaseDynamicTableFactory.java
+++ b/flink-connectors/flink-connector-hbase/src/main/java/org/apache/flink/connector/hbase/HBaseDynamicTableFactory.java
@@ -54,64 +54,65 @@ public class HBaseDynamicTableFactory implements DynamicTableSourceFactory, Dyna
.key("table-name")
.stringType()
.noDefaultValue()
- .withDescription("Required. It defines the HBase table name.");
+ .withDescription("The name of HBase table to connect.");
private static final ConfigOption ZOOKEEPER_QUORUM = ConfigOptions
.key("zookeeper.quorum")
.stringType()
.noDefaultValue()
- .withDescription("Required. It defines HBase Zookeeper quorum.");
+ .withDescription("The HBase Zookeeper quorum.");
private static final ConfigOption ZOOKEEPER_ZNODE_PARENT = ConfigOptions
- .key("zookeeper.znode-parent")
+ .key("zookeeper.znode.parent")
.stringType()
.defaultValue("/hbase")
- .withDescription("Optional. The root dir in Zookeeper for HBase cluster, default value is '/hbase'");
+ .withDescription("The root dir in Zookeeper for HBase cluster.");
private static final ConfigOption NULL_STRING_LITERAL = ConfigOptions
.key("null-string-literal")
.stringType()
.defaultValue("null")
- .withDescription("Optional. Representation for null values for string fields. (\"null\" by default). " +
- "HBase connector encode/decode empty bytes as null values except string types.");
+ .withDescription("Representation for null values for string fields. HBase source and " +
+ "sink encodes/decodes empty bytes as null values for all types except string type.");
private static final ConfigOption SINK_BUFFER_FLUSH_MAX_SIZE = ConfigOptions
.key("sink.buffer-flush.max-size")
.memoryType()
.defaultValue(MemorySize.parse("2mb"))
- .withDescription("Optional. Writing option, determines how many size in memory of " +
- "buffered rows to insert per round trip. This can help performance on writing " +
- "to JDBC database. The default value is '2mb'.");
+ .withDescription("Writing option, maximum size in memory of buffered rows for each " +
+ "writing request. This can improve performance for writing data to HBase database, " +
+ "but may increase the latency. Can be set to '0' to disable it. ");
private static final ConfigOption SINK_BUFFER_FLUSH_MAX_ROWS = ConfigOptions
.key("sink.buffer-flush.max-rows")
.intType()
- .noDefaultValue()
- .withDescription("Optional. Writing option, determines how many rows to insert " +
- "per round trip. This can help performance on writing to JDBC database. " +
- "No default value, i.e. the default flushing is not depends on the number of buffered rows.");
+ .defaultValue(1000)
+ .withDescription("Writing option, maximum number of rows to buffer for each writing request. " +
+ "This can improve performance for writing data to HBase database, but may increase the latency. " +
+ "Can be set to '0' to disable it.");
private static final ConfigOption SINK_BUFFER_FLUSH_INTERVAL = ConfigOptions
.key("sink.buffer-flush.interval")
.durationType()
- .noDefaultValue()
- .withDescription("Optional. Writing option, sets a flush interval flushing " +
- "buffered requesting if the interval passes, in milliseconds. Default value is '0s', " +
- "which means no asynchronous flush thread will be scheduled.");
+ .defaultValue(Duration.ofSeconds(1))
+ .withDescription("Writing option, the interval to flush any buffered rows. " +
+ "This can improve performance for writing data to HBase database, but may increase the latency. " +
+ "Can be set to '0' to disable it. Note, both 'sink.buffer-flush.max-size' and 'sink.buffer-flush.max-rows' " +
+ "can be set to '0' with the flush interval set allowing for complete async processing of buffered actions.");
@Override
public DynamicTableSource createDynamicTableSource(Context context) {
TableFactoryHelper helper = createTableFactoryHelper(this, context);
helper.validate();
+ TableSchema tableSchema = context.getCatalogTable().getSchema();
+ validatePrimaryKey(tableSchema);
+
String hTableName = helper.getOptions().get(TABLE_NAME);
// create default configuration from current runtime env (`hbase-site.xml` in classpath) first,
Configuration hbaseClientConf = HBaseConfiguration.create();
hbaseClientConf.set(HConstants.ZOOKEEPER_QUORUM, helper.getOptions().get(ZOOKEEPER_QUORUM));
hbaseClientConf.set(HConstants.ZOOKEEPER_ZNODE_PARENT, helper.getOptions().get(ZOOKEEPER_ZNODE_PARENT));
-
String nullStringLiteral = helper.getOptions().get(NULL_STRING_LITERAL);
-
- TableSchema tableSchema = context.getCatalogTable().getSchema();
HBaseTableSchema hbaseSchema = HBaseTableSchema.fromTableSchema(tableSchema);
return new HBaseDynamicTableSource(
@@ -125,6 +126,9 @@ public DynamicTableSource createDynamicTableSource(Context context) {
public DynamicTableSink createDynamicTableSink(Context context) {
TableFactoryHelper helper = createTableFactoryHelper(this, context);
helper.validate();
+ TableSchema tableSchema = context.getCatalogTable().getSchema();
+ validatePrimaryKey(tableSchema);
+
HBaseOptions.Builder hbaseOptionsBuilder = HBaseOptions.builder();
hbaseOptionsBuilder.setTableName(helper.getOptions().get(TABLE_NAME));
hbaseOptionsBuilder.setZkQuorum(helper.getOptions().get(ZOOKEEPER_QUORUM));
@@ -132,14 +136,9 @@ public DynamicTableSink createDynamicTableSink(Context context) {
HBaseWriteOptions.Builder writeBuilder = HBaseWriteOptions.builder();
writeBuilder.setBufferFlushMaxSizeInBytes(helper.getOptions().get(SINK_BUFFER_FLUSH_MAX_SIZE).getBytes());
- helper.getOptions().getOptional(SINK_BUFFER_FLUSH_INTERVAL)
- .ifPresent(v -> writeBuilder.setBufferFlushIntervalMillis(v.toMillis()));
- helper.getOptions().getOptional(SINK_BUFFER_FLUSH_MAX_ROWS)
- .ifPresent(writeBuilder::setBufferFlushMaxRows);
-
+ writeBuilder.setBufferFlushIntervalMillis(helper.getOptions().get(SINK_BUFFER_FLUSH_INTERVAL).toMillis());
+ writeBuilder.setBufferFlushMaxRows(helper.getOptions().get(SINK_BUFFER_FLUSH_MAX_ROWS));
String nullStringLiteral = helper.getOptions().get(NULL_STRING_LITERAL);
-
- TableSchema tableSchema = context.getCatalogTable().getSchema();
HBaseTableSchema hbaseSchema = HBaseTableSchema.fromTableSchema(tableSchema);
return new HBaseDynamicTableSink(
@@ -172,4 +171,35 @@ public Set> optionalOptions() {
set.add(SINK_BUFFER_FLUSH_INTERVAL);
return set;
}
+
+ // ------------------------------------------------------------------------------------------
+
+ /**
+ * Checks that the HBase table have row key defined. A row key is defined as an atomic type,
+ * and column families and qualifiers are defined as ROW type. There shouldn't be multiple
+ * atomic type columns in the schema. The PRIMARY KEY constraint is optional, if exist, the
+ * primary key constraint must be defined on the single row key field.
+ */
+ private static void validatePrimaryKey(TableSchema schema) {
+ HBaseTableSchema hbaseSchema = HBaseTableSchema.fromTableSchema(schema);
+ if (!hbaseSchema.getRowKeyName().isPresent()) {
+ throw new IllegalArgumentException(
+ "HBase table requires to define a row key field. " +
+ "A row key field is defined as an atomic type, " +
+ "column families and qualifiers are defined as ROW type.");
+ }
+ schema.getPrimaryKey().ifPresent(k -> {
+ if (k.getColumns().size() > 1) {
+ throw new IllegalArgumentException(
+ "HBase table doesn't support a primary Key on multiple columns. " +
+ "The primary key of HBase table must be defined on row key field.");
+ }
+ if (!hbaseSchema.getRowKeyName().get().equals(k.getColumns().get(0))) {
+ throw new IllegalArgumentException(
+ "Primary key of HBase table must be defined on the row key field. " +
+ "A row key field is defined as an atomic type, " +
+ "column families and qualifiers are defined as ROW type.");
+ }
+ });
+ }
}
diff --git a/flink-connectors/flink-connector-hbase/src/main/java/org/apache/flink/connector/hbase/HBaseTableFactory.java b/flink-connectors/flink-connector-hbase/src/main/java/org/apache/flink/connector/hbase/HBaseTableFactory.java
index 64227d39e1c51f..f64f9b9f6da734 100644
--- a/flink-connectors/flink-connector-hbase/src/main/java/org/apache/flink/connector/hbase/HBaseTableFactory.java
+++ b/flink-connectors/flink-connector-hbase/src/main/java/org/apache/flink/connector/hbase/HBaseTableFactory.java
@@ -55,7 +55,7 @@
import static org.apache.flink.table.descriptors.ConnectorDescriptorValidator.CONNECTOR_PROPERTY_VERSION;
import static org.apache.flink.table.descriptors.ConnectorDescriptorValidator.CONNECTOR_TYPE;
import static org.apache.flink.table.descriptors.ConnectorDescriptorValidator.CONNECTOR_VERSION;
-import static org.apache.flink.table.descriptors.DescriptorProperties.TABLE_SCHEMA_EXPR;
+import static org.apache.flink.table.descriptors.DescriptorProperties.EXPR;
import static org.apache.flink.table.descriptors.DescriptorProperties.WATERMARK;
import static org.apache.flink.table.descriptors.DescriptorProperties.WATERMARK_ROWTIME;
import static org.apache.flink.table.descriptors.DescriptorProperties.WATERMARK_STRATEGY_DATA_TYPE;
@@ -192,13 +192,17 @@ public List supportedProperties() {
properties.add(SCHEMA + ".#." + SCHEMA_TYPE);
properties.add(SCHEMA + ".#." + SCHEMA_NAME);
// computed column
- properties.add(SCHEMA + ".#." + TABLE_SCHEMA_EXPR);
+ properties.add(SCHEMA + ".#." + EXPR);
// watermark
properties.add(SCHEMA + "." + WATERMARK + ".#." + WATERMARK_ROWTIME);
properties.add(SCHEMA + "." + WATERMARK + ".#." + WATERMARK_STRATEGY_EXPR);
properties.add(SCHEMA + "." + WATERMARK + ".#." + WATERMARK_STRATEGY_DATA_TYPE);
+ // table constraint
+ properties.add(SCHEMA + "." + DescriptorProperties.PRIMARY_KEY_NAME);
+ properties.add(SCHEMA + "." + DescriptorProperties.PRIMARY_KEY_COLUMNS);
+
return properties;
}
diff --git a/flink-connectors/flink-connector-hbase/src/main/java/org/apache/flink/connector/hbase/options/HBaseWriteOptions.java b/flink-connectors/flink-connector-hbase/src/main/java/org/apache/flink/connector/hbase/options/HBaseWriteOptions.java
index f4143d4780256b..3f7c0a962745e2 100644
--- a/flink-connectors/flink-connector-hbase/src/main/java/org/apache/flink/connector/hbase/options/HBaseWriteOptions.java
+++ b/flink-connectors/flink-connector-hbase/src/main/java/org/apache/flink/connector/hbase/options/HBaseWriteOptions.java
@@ -25,8 +25,6 @@
import java.io.Serializable;
import java.util.Objects;
-import static org.apache.flink.util.Preconditions.checkArgument;
-
/**
* Options for HBase writing.
*/
@@ -100,19 +98,15 @@ public static Builder builder() {
*/
public static class Builder {
- // default is 2mb which is defined in hbase
private long bufferFlushMaxSizeInBytes = ConnectionConfiguration.WRITE_BUFFER_SIZE_DEFAULT;
- private long bufferFlushMaxRows = -1;
- private long bufferFlushIntervalMillis = -1;
+ private long bufferFlushMaxRows = 0;
+ private long bufferFlushIntervalMillis = 0;
/**
* Optional. Sets when to flush a buffered request based on the memory size of rows currently added.
* Default to 2mb.
*/
public Builder setBufferFlushMaxSizeInBytes(long bufferFlushMaxSizeInBytes) {
- checkArgument(
- bufferFlushMaxSizeInBytes > 0,
- "Max byte size of buffered rows must be larger than 0.");
this.bufferFlushMaxSizeInBytes = bufferFlushMaxSizeInBytes;
return this;
}
@@ -122,9 +116,6 @@ public Builder setBufferFlushMaxSizeInBytes(long bufferFlushMaxSizeInBytes) {
* Defaults to not set, i.e. won't flush based on the number of buffered rows.
*/
public Builder setBufferFlushMaxRows(long bufferFlushMaxRows) {
- checkArgument(
- bufferFlushMaxRows > 0,
- "Max number of buffered rows must be larger than 0.");
this.bufferFlushMaxRows = bufferFlushMaxRows;
return this;
}
@@ -134,9 +125,6 @@ public Builder setBufferFlushMaxRows(long bufferFlushMaxRows) {
* Defaults to not set, i.e. won't flush based on flush interval.
*/
public Builder setBufferFlushIntervalMillis(long bufferFlushIntervalMillis) {
- checkArgument(
- bufferFlushIntervalMillis > 0,
- "Interval (in milliseconds) between each flush must be larger than 0.");
this.bufferFlushIntervalMillis = bufferFlushIntervalMillis;
return this;
}
diff --git a/flink-connectors/flink-connector-hbase/src/main/java/org/apache/flink/connector/hbase/sink/HBaseSinkFunction.java b/flink-connectors/flink-connector-hbase/src/main/java/org/apache/flink/connector/hbase/sink/HBaseSinkFunction.java
index 3d96e5d0916310..3ba533ae47909b 100644
--- a/flink-connectors/flink-connector-hbase/src/main/java/org/apache/flink/connector/hbase/sink/HBaseSinkFunction.java
+++ b/flink-connectors/flink-connector-hbase/src/main/java/org/apache/flink/connector/hbase/sink/HBaseSinkFunction.java
@@ -118,8 +118,10 @@ public void open(Configuration parameters) throws Exception {
}
// create a parameter instance, set the table name and custom listener reference.
BufferedMutatorParams params = new BufferedMutatorParams(TableName.valueOf(hTableName))
- .listener(this)
- .writeBufferSize(bufferFlushMaxSizeInBytes);
+ .listener(this);
+ if (bufferFlushMaxSizeInBytes > 0) {
+ params.writeBufferSize(bufferFlushMaxSizeInBytes);
+ }
this.mutator = connection.getBufferedMutator(params);
if (bufferFlushIntervalMillis > 0) {
diff --git a/flink-connectors/flink-connector-hbase/src/main/java/org/apache/flink/connector/hbase/source/AbstractTableInputFormat.java b/flink-connectors/flink-connector-hbase/src/main/java/org/apache/flink/connector/hbase/source/AbstractTableInputFormat.java
index 684afd2220240e..94d36d3c06bcca 100644
--- a/flink-connectors/flink-connector-hbase/src/main/java/org/apache/flink/connector/hbase/source/AbstractTableInputFormat.java
+++ b/flink-connectors/flink-connector-hbase/src/main/java/org/apache/flink/connector/hbase/source/AbstractTableInputFormat.java
@@ -24,8 +24,10 @@
import org.apache.flink.api.common.io.RichInputFormat;
import org.apache.flink.api.common.io.statistics.BaseStatistics;
import org.apache.flink.configuration.Configuration;
+import org.apache.flink.connector.hbase.util.HBaseConfigurationUtil;
import org.apache.flink.core.io.InputSplitAssigner;
+import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.client.HTable;
import org.apache.hadoop.hbase.client.Result;
import org.apache.hadoop.hbase.client.ResultScanner;
@@ -60,6 +62,13 @@ abstract class AbstractTableInputFormat extends RichInputFormat extends RichInputFormat extends AbstractTableInp
private static final long serialVersionUID = 1L;
+ /**
+ * Constructs a {@link InputFormat} with hbase configuration to read data from hbase.
+ * @param hConf The configuration that connect to hbase.
+ * At least hbase.zookeeper.quorum and zookeeper.znode.parent need to be set.
+ */
+ public HBaseInputFormat(org.apache.hadoop.conf.Configuration hConf) {
+ super(hConf);
+ }
+
/**
* Returns an instance of Scan that retrieves the required subset of records from the HBase table.
* @return The appropriate instance of Scan for this usecase.
@@ -79,8 +87,7 @@ public void configure(Configuration parameters) {
*/
private HTable createTable() {
LOG.info("Initializing HBaseConfiguration");
- //use files found in the classpath
- org.apache.hadoop.conf.Configuration hConf = HBaseConfiguration.create();
+ org.apache.hadoop.conf.Configuration hConf = getHadoopConfiguration();
try {
return new HTable(hConf, getTableName());
diff --git a/flink-connectors/flink-connector-hbase/src/main/java/org/apache/flink/connector/hbase/source/HBaseLookupFunction.java b/flink-connectors/flink-connector-hbase/src/main/java/org/apache/flink/connector/hbase/source/HBaseLookupFunction.java
index 1d608e9fd51170..c84cbe1dc747d8 100644
--- a/flink-connectors/flink-connector-hbase/src/main/java/org/apache/flink/connector/hbase/source/HBaseLookupFunction.java
+++ b/flink-connectors/flink-connector-hbase/src/main/java/org/apache/flink/connector/hbase/source/HBaseLookupFunction.java
@@ -45,7 +45,7 @@
/**
* The HBaseLookupFunction is a standard user-defined table function, it can be used in tableAPI
- * and also useful for temporal table join plan in SQL.
+ * and also useful for temporal table join plan in SQL. It looks up the result as {@link Row}.
*/
@Internal
public class HBaseLookupFunction extends TableFunction {
diff --git a/flink-connectors/flink-connector-hbase/src/main/java/org/apache/flink/connector/hbase/source/HBaseRowDataInputFormat.java b/flink-connectors/flink-connector-hbase/src/main/java/org/apache/flink/connector/hbase/source/HBaseRowDataInputFormat.java
index 5a628d9dfe22c4..30be6d87a01c0b 100644
--- a/flink-connectors/flink-connector-hbase/src/main/java/org/apache/flink/connector/hbase/source/HBaseRowDataInputFormat.java
+++ b/flink-connectors/flink-connector-hbase/src/main/java/org/apache/flink/connector/hbase/source/HBaseRowDataInputFormat.java
@@ -24,7 +24,6 @@
import org.apache.flink.connector.hbase.util.HBaseTableSchema;
import org.apache.flink.table.data.RowData;
-import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.TableNotFoundException;
import org.apache.hadoop.hbase.client.Connection;
@@ -50,15 +49,13 @@ public class HBaseRowDataInputFormat extends AbstractTableInputFormat {
private transient HBaseSerde serde;
- private transient org.apache.hadoop.conf.Configuration conf;
-
public HBaseRowDataInputFormat(
org.apache.hadoop.conf.Configuration conf,
String tableName,
HBaseTableSchema schema,
String nullStringLiteral) {
+ super(conf);
this.tableName = tableName;
- this.conf = conf;
this.schema = schema;
this.nullStringLiteral = nullStringLiteral;
}
@@ -89,13 +86,8 @@ protected RowData mapResultToOutType(Result res) {
}
private void connectToTable() {
-
- if (this.conf == null) {
- this.conf = HBaseConfiguration.create();
- }
-
try {
- Connection conn = ConnectionFactory.createConnection(conf);
+ Connection conn = ConnectionFactory.createConnection(getHadoopConfiguration());
super.table = (HTable) conn.getTable(TableName.valueOf(tableName));
} catch (TableNotFoundException tnfe) {
LOG.error("The table " + tableName + " not found ", tnfe);
diff --git a/flink-connectors/flink-connector-hbase/src/main/java/org/apache/flink/connector/hbase/source/HBaseRowDataLookupFunction.java b/flink-connectors/flink-connector-hbase/src/main/java/org/apache/flink/connector/hbase/source/HBaseRowDataLookupFunction.java
new file mode 100644
index 00000000000000..7facf58778975c
--- /dev/null
+++ b/flink-connectors/flink-connector-hbase/src/main/java/org/apache/flink/connector/hbase/source/HBaseRowDataLookupFunction.java
@@ -0,0 +1,155 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.flink.connector.hbase.source;
+
+import org.apache.flink.annotation.Internal;
+import org.apache.flink.annotation.VisibleForTesting;
+import org.apache.flink.connector.hbase.util.HBaseConfigurationUtil;
+import org.apache.flink.connector.hbase.util.HBaseSerde;
+import org.apache.flink.connector.hbase.util.HBaseTableSchema;
+import org.apache.flink.table.data.RowData;
+import org.apache.flink.table.functions.FunctionContext;
+import org.apache.flink.table.functions.TableFunction;
+import org.apache.flink.util.StringUtils;
+
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.hbase.HBaseConfiguration;
+import org.apache.hadoop.hbase.HConstants;
+import org.apache.hadoop.hbase.TableName;
+import org.apache.hadoop.hbase.TableNotFoundException;
+import org.apache.hadoop.hbase.client.Connection;
+import org.apache.hadoop.hbase.client.ConnectionFactory;
+import org.apache.hadoop.hbase.client.Get;
+import org.apache.hadoop.hbase.client.HTable;
+import org.apache.hadoop.hbase.client.Result;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+
+/**
+ * The HBaseRowDataLookupFunction is a standard user-defined table function, it can be used in tableAPI
+ * and also useful for temporal table join plan in SQL. It looks up the result as {@link RowData}.
+ */
+@Internal
+public class HBaseRowDataLookupFunction extends TableFunction {
+
+ private static final Logger LOG = LoggerFactory.getLogger(HBaseRowDataLookupFunction.class);
+ private static final long serialVersionUID = 1L;
+
+ private final String hTableName;
+ private final byte[] serializedConfig;
+ private final HBaseTableSchema hbaseTableSchema;
+ private final String nullStringLiteral;
+
+ private transient Connection hConnection;
+ private transient HTable table;
+ private transient HBaseSerde serde;
+
+ public HBaseRowDataLookupFunction(
+ Configuration configuration,
+ String hTableName,
+ HBaseTableSchema hbaseTableSchema,
+ String nullStringLiteral) {
+ this.serializedConfig = HBaseConfigurationUtil.serializeConfiguration(configuration);
+ this.hTableName = hTableName;
+ this.hbaseTableSchema = hbaseTableSchema;
+ this.nullStringLiteral = nullStringLiteral;
+ }
+
+ /**
+ * The invoke entry point of lookup function.
+ * @param rowKey the lookup key. Currently only support single rowkey.
+ */
+ public void eval(Object rowKey) throws IOException {
+ // fetch result
+ Get get = serde.createGet(rowKey);
+ if (get != null) {
+ Result result = table.get(get);
+ if (!result.isEmpty()) {
+ // parse and collect
+ collect(serde.convertToRow(result));
+ }
+ }
+ }
+
+ private Configuration prepareRuntimeConfiguration() {
+ // create default configuration from current runtime env (`hbase-site.xml` in classpath) first,
+ // and overwrite configuration using serialized configuration from client-side env (`hbase-site.xml` in classpath).
+ // user params from client-side have the highest priority
+ org.apache.hadoop.conf.Configuration runtimeConfig = HBaseConfigurationUtil.deserializeConfiguration(
+ serializedConfig,
+ HBaseConfiguration.create());
+
+ // do validation: check key option(s) in final runtime configuration
+ if (StringUtils.isNullOrWhitespaceOnly(runtimeConfig.get(HConstants.ZOOKEEPER_QUORUM))) {
+ LOG.error("can not connect to HBase without {} configuration", HConstants.ZOOKEEPER_QUORUM);
+ throw new IllegalArgumentException("check HBase configuration failed, lost: '" + HConstants.ZOOKEEPER_QUORUM + "'!");
+ }
+
+ return runtimeConfig;
+ }
+
+ @Override
+ public void open(FunctionContext context) {
+ LOG.info("start open ...");
+ Configuration config = prepareRuntimeConfiguration();
+ try {
+ hConnection = ConnectionFactory.createConnection(config);
+ table = (HTable) hConnection.getTable(TableName.valueOf(hTableName));
+ } catch (TableNotFoundException tnfe) {
+ LOG.error("Table '{}' not found ", hTableName, tnfe);
+ throw new RuntimeException("HBase table '" + hTableName + "' not found.", tnfe);
+ } catch (IOException ioe) {
+ LOG.error("Exception while creating connection to HBase.", ioe);
+ throw new RuntimeException("Cannot create connection to HBase.", ioe);
+ }
+ this.serde = new HBaseSerde(hbaseTableSchema, nullStringLiteral);
+ LOG.info("end open.");
+ }
+
+ @Override
+ public void close() {
+ LOG.info("start close ...");
+ if (null != table) {
+ try {
+ table.close();
+ table = null;
+ } catch (IOException e) {
+ // ignore exception when close.
+ LOG.warn("exception when close table", e);
+ }
+ }
+ if (null != hConnection) {
+ try {
+ hConnection.close();
+ hConnection = null;
+ } catch (IOException e) {
+ // ignore exception when close.
+ LOG.warn("exception when close connection", e);
+ }
+ }
+ LOG.info("end close.");
+ }
+
+ @VisibleForTesting
+ public String getHTableName() {
+ return hTableName;
+ }
+}
diff --git a/flink-connectors/flink-connector-hbase/src/main/java/org/apache/flink/connector/hbase/source/HBaseRowInputFormat.java b/flink-connectors/flink-connector-hbase/src/main/java/org/apache/flink/connector/hbase/source/HBaseRowInputFormat.java
index f4552986e4c870..f7100ed7a8f2aa 100644
--- a/flink-connectors/flink-connector-hbase/src/main/java/org/apache/flink/connector/hbase/source/HBaseRowInputFormat.java
+++ b/flink-connectors/flink-connector-hbase/src/main/java/org/apache/flink/connector/hbase/source/HBaseRowInputFormat.java
@@ -28,7 +28,6 @@
import org.apache.flink.connector.hbase.util.HBaseTableSchema;
import org.apache.flink.types.Row;
-import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.TableNotFoundException;
import org.apache.hadoop.hbase.client.Connection;
@@ -54,12 +53,11 @@ public class HBaseRowInputFormat extends AbstractTableInputFormat implement
private final String tableName;
private final HBaseTableSchema schema;
- private transient org.apache.hadoop.conf.Configuration conf;
private transient HBaseReadWriteHelper readHelper;
public HBaseRowInputFormat(org.apache.hadoop.conf.Configuration conf, String tableName, HBaseTableSchema schema) {
+ super(conf);
this.tableName = tableName;
- this.conf = conf;
this.schema = schema;
}
@@ -90,13 +88,8 @@ protected Row mapResultToOutType(Result res) {
}
private void connectToTable() {
-
- if (this.conf == null) {
- this.conf = HBaseConfiguration.create();
- }
-
try {
- Connection conn = ConnectionFactory.createConnection(conf);
+ Connection conn = ConnectionFactory.createConnection(getHadoopConfiguration());
super.table = (HTable) conn.getTable(TableName.valueOf(tableName));
} catch (TableNotFoundException tnfe) {
LOG.error("The table " + tableName + " not found ", tnfe);
diff --git a/flink-connectors/flink-connector-hbase/src/main/java/org/apache/flink/connector/hbase/util/HBaseSerde.java b/flink-connectors/flink-connector-hbase/src/main/java/org/apache/flink/connector/hbase/util/HBaseSerde.java
index ed4a11f6b74882..f88891430c1749 100644
--- a/flink-connectors/flink-connector-hbase/src/main/java/org/apache/flink/connector/hbase/util/HBaseSerde.java
+++ b/flink-connectors/flink-connector-hbase/src/main/java/org/apache/flink/connector/hbase/util/HBaseSerde.java
@@ -29,6 +29,7 @@
import org.apache.flink.table.types.logical.LogicalTypeFamily;
import org.apache.hadoop.hbase.client.Delete;
+import org.apache.hadoop.hbase.client.Get;
import org.apache.hadoop.hbase.client.Put;
import org.apache.hadoop.hbase.client.Result;
import org.apache.hadoop.hbase.client.Scan;
@@ -52,6 +53,11 @@ public class HBaseSerde {
private static final byte[] EMPTY_BYTES = new byte[]{};
+ private static final int MIN_TIMESTAMP_PRECISION = 0;
+ private static final int MAX_TIMESTAMP_PRECISION = 3;
+ private static final int MIN_TIME_PRECISION = 0;
+ private static final int MAX_TIME_PRECISION = 3;
+
private final byte[] nullStringBytes;
// row key index in output row
@@ -67,10 +73,11 @@ public class HBaseSerde {
private GenericRowData reusedRow;
private GenericRowData[] reusedFamilyRows;
- private final FieldEncoder keyEncoder;
- private final FieldDecoder keyDecoder;
+ private final @Nullable FieldEncoder keyEncoder;
+ private final @Nullable FieldDecoder keyDecoder;
private final FieldEncoder[][] qualifierEncoders;
private final FieldDecoder[][] qualifierDecoders;
+ private final GenericRowData rowWithRowKey;
public HBaseSerde(HBaseTableSchema hbaseSchema, final String nullStringLiteral) {
this.families = hbaseSchema.getFamilyKeys();
@@ -78,18 +85,21 @@ public HBaseSerde(HBaseTableSchema hbaseSchema, final String nullStringLiteral)
LogicalType rowkeyType = hbaseSchema.getRowKeyDataType().map(DataType::getLogicalType).orElse(null);
// field length need take row key into account if it exists.
- checkArgument(rowkeyIndex != -1 && rowkeyType != null, "row key is not set.");
- this.fieldLength = families.length + 1;
+ if (rowkeyIndex != -1 && rowkeyType != null) {
+ this.fieldLength = families.length + 1;
+ this.keyEncoder = createFieldEncoder(rowkeyType);
+ this.keyDecoder = createFieldDecoder(rowkeyType);
+ } else {
+ this.fieldLength = families.length;
+ this.keyEncoder = null;
+ this.keyDecoder = null;
+ }
this.nullStringBytes = nullStringLiteral.getBytes(StandardCharsets.UTF_8);
// prepare output rows
this.reusedRow = new GenericRowData(fieldLength);
this.reusedFamilyRows = new GenericRowData[families.length];
- // row key should never be null
- this.keyEncoder = createFieldEncoder(rowkeyType);
- this.keyDecoder = createFieldDecoder(rowkeyType);
-
this.qualifiers = new byte[families.length][][];
this.qualifierEncoders = new FieldEncoder[families.length][];
this.qualifierDecoders = new FieldDecoder[families.length][];
@@ -107,6 +117,7 @@ public HBaseSerde(HBaseTableSchema hbaseSchema, final String nullStringLiteral)
.toArray(FieldDecoder[]::new);
this.reusedFamilyRows[f] = new GenericRowData(dataTypes.length);
}
+ this.rowWithRowKey = new GenericRowData(1);
}
/**
@@ -115,6 +126,7 @@ public HBaseSerde(HBaseTableSchema hbaseSchema, final String nullStringLiteral)
* @return The appropriate instance of Put for this use case.
*/
public @Nullable Put createPutMutation(RowData row) {
+ checkArgument(keyEncoder != null, "row key is not set.");
byte[] rowkey = keyEncoder.encode(row, rowkeyIndex);
if (rowkey.length == 0) {
// drop dirty records, rowkey shouldn't be zero length
@@ -146,6 +158,7 @@ public HBaseSerde(HBaseTableSchema hbaseSchema, final String nullStringLiteral)
* @return The appropriate instance of Delete for this use case.
*/
public @Nullable Delete createDeleteMutation(RowData row) {
+ checkArgument(keyEncoder != null, "row key is not set.");
byte[] rowkey = keyEncoder.encode(row, rowkeyIndex);
if (rowkey.length == 0) {
// drop dirty records, rowkey shouldn't be zero length
@@ -185,13 +198,37 @@ public Scan createScan() {
return scan;
}
+ /**
+ * Returns an instance of Get that retrieves the matches records from the HBase table.
+ *
+ * @return The appropriate instance of Get for this use case.
+ */
+ public Get createGet(Object rowKey) {
+ checkArgument(keyEncoder != null, "row key is not set.");
+ rowWithRowKey.setField(0, rowKey);
+ byte[] rowkey = keyEncoder.encode(rowWithRowKey, 0);
+ if (rowkey.length == 0) {
+ // drop dirty records, rowkey shouldn't be zero length
+ return null;
+ }
+ Get get = new Get(rowkey);
+ for (int f = 0; f < families.length; f++) {
+ byte[] family = families[f];
+ for (byte[] qualifier : qualifiers[f]) {
+ get.addColumn(family, qualifier);
+ }
+ }
+ return get;
+ }
+
/**
* Converts HBase {@link Result} into {@link RowData}.
*/
public RowData convertToRow(Result result) {
- Object rowkey = keyDecoder.decode(result.getRow());
for (int i = 0; i < fieldLength; i++) {
if (rowkeyIndex == i) {
+ assert keyDecoder != null;
+ Object rowkey = keyDecoder.decode(result.getRow());
reusedRow.setField(rowkeyIndex, rowkey);
} else {
int f = (rowkeyIndex != -1 && i > rowkeyIndex) ? i - 1 : i;
@@ -270,9 +307,16 @@ private static FieldEncoder createFieldEncoder(LogicalType fieldType) {
return (row, pos) -> Bytes.toBytes(row.getShort(pos));
case INTEGER:
case DATE:
- case TIME_WITHOUT_TIME_ZONE:
case INTERVAL_YEAR_MONTH:
return (row, pos) -> Bytes.toBytes(row.getInt(pos));
+ case TIME_WITHOUT_TIME_ZONE:
+ final int timePrecision = getPrecision(fieldType);
+ if (timePrecision < MIN_TIME_PRECISION || timePrecision > MAX_TIME_PRECISION) {
+ throw new UnsupportedOperationException(
+ String.format("The precision %s of TIME type is out of the range [%s, %s] supported by " +
+ "HBase connector", timePrecision, MIN_TIME_PRECISION, MAX_TIME_PRECISION));
+ }
+ return (row, pos) -> Bytes.toBytes(row.getInt(pos));
case BIGINT:
case INTERVAL_DAY_TIME:
return (row, pos) -> Bytes.toBytes(row.getLong(pos));
@@ -282,7 +326,13 @@ private static FieldEncoder createFieldEncoder(LogicalType fieldType) {
return (row, pos) -> Bytes.toBytes(row.getDouble(pos));
case TIMESTAMP_WITHOUT_TIME_ZONE:
case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
- return createTimestampEncoder(getPrecision(fieldType));
+ final int timestampPrecision = getPrecision(fieldType);
+ if (timestampPrecision < MIN_TIMESTAMP_PRECISION || timestampPrecision > MAX_TIMESTAMP_PRECISION) {
+ throw new UnsupportedOperationException(
+ String.format("The precision %s of TIMESTAMP type is out of the range [%s, %s] supported by " +
+ "HBase connector", timestampPrecision, MIN_TIMESTAMP_PRECISION, MAX_TIMESTAMP_PRECISION));
+ }
+ return createTimestampEncoder(timestampPrecision);
default:
throw new UnsupportedOperationException("Unsupported type: " + fieldType);
}
@@ -361,9 +411,16 @@ private static FieldDecoder createFieldDecoder(LogicalType fieldType) {
return Bytes::toShort;
case INTEGER:
case DATE:
- case TIME_WITHOUT_TIME_ZONE:
case INTERVAL_YEAR_MONTH:
return Bytes::toInt;
+ case TIME_WITHOUT_TIME_ZONE:
+ final int timePrecision = getPrecision(fieldType);
+ if (timePrecision < MIN_TIME_PRECISION || timePrecision > MAX_TIME_PRECISION) {
+ throw new UnsupportedOperationException(
+ String.format("The precision %s of TIME type is out of the range [%s, %s] supported by " +
+ "HBase connector", timePrecision, MIN_TIME_PRECISION, MAX_TIME_PRECISION));
+ }
+ return Bytes::toInt;
case BIGINT:
case INTERVAL_DAY_TIME:
return Bytes::toLong;
@@ -373,6 +430,12 @@ private static FieldDecoder createFieldDecoder(LogicalType fieldType) {
return Bytes::toDouble;
case TIMESTAMP_WITHOUT_TIME_ZONE:
case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
+ final int timestampPrecision = getPrecision(fieldType);
+ if (timestampPrecision < MIN_TIMESTAMP_PRECISION || timestampPrecision > MAX_TIMESTAMP_PRECISION) {
+ throw new UnsupportedOperationException(
+ String.format("The precision %s of TIMESTAMP type is out of the range [%s, %s] supported by " +
+ "HBase connector", timestampPrecision, MIN_TIMESTAMP_PRECISION, MAX_TIMESTAMP_PRECISION));
+ }
return createTimestampDecoder();
default:
throw new UnsupportedOperationException("Unsupported type: " + fieldType);
diff --git a/flink-connectors/flink-connector-hbase/src/main/java/org/apache/flink/connector/hbase/util/HBaseTableSchema.java b/flink-connectors/flink-connector-hbase/src/main/java/org/apache/flink/connector/hbase/util/HBaseTableSchema.java
index 41108f4bbbc82f..116b1ae374fd08 100644
--- a/flink-connectors/flink-connector-hbase/src/main/java/org/apache/flink/connector/hbase/util/HBaseTableSchema.java
+++ b/flink-connectors/flink-connector-hbase/src/main/java/org/apache/flink/connector/hbase/util/HBaseTableSchema.java
@@ -361,18 +361,6 @@ public static HBaseTableSchema fromTableSchema(TableSchema schema) {
"Unsupported field type '" + fieldType + "' for HBase.");
}
}
- schema.getPrimaryKey().ifPresent(k -> {
- if (k.getColumns().size() > 1 ||
- !hbaseSchema.getRowKeyName().isPresent() ||
- !hbaseSchema.getRowKeyName().get().equals(k.getColumns().get(0))) {
- throw new IllegalArgumentException(
- "Primary Key of HBase table should only be defined on the row key field.");
- }
- });
- if (!hbaseSchema.getRowKeyName().isPresent()) {
- throw new IllegalArgumentException(
- "HBase table requires to define a row key field. A row key field must be an atomic type.");
- }
return hbaseSchema;
}
diff --git a/flink-connectors/flink-connector-hbase/src/main/java/org/apache/flink/connector/hbase/util/HBaseTypeUtils.java b/flink-connectors/flink-connector-hbase/src/main/java/org/apache/flink/connector/hbase/util/HBaseTypeUtils.java
index ec975973da47b9..0665e4b6f8ebf0 100644
--- a/flink-connectors/flink-connector-hbase/src/main/java/org/apache/flink/connector/hbase/util/HBaseTypeUtils.java
+++ b/flink-connectors/flink-connector-hbase/src/main/java/org/apache/flink/connector/hbase/util/HBaseTypeUtils.java
@@ -42,6 +42,11 @@ public class HBaseTypeUtils {
private static final byte[] EMPTY_BYTES = new byte[]{};
+ private static final int MIN_TIMESTAMP_PRECISION = 0;
+ private static final int MAX_TIMESTAMP_PRECISION = 3;
+ private static final int MIN_TIME_PRECISION = 0;
+ private static final int MAX_TIME_PRECISION = 3;
+
/**
* Deserialize byte array to Java Object with the given type.
*/
@@ -184,16 +189,29 @@ public static boolean isSupportedType(LogicalType type) {
case SMALLINT:
case INTEGER:
case DATE:
- case TIME_WITHOUT_TIME_ZONE:
case INTERVAL_YEAR_MONTH:
case BIGINT:
case INTERVAL_DAY_TIME:
case FLOAT:
case DOUBLE:
return true;
+ case TIME_WITHOUT_TIME_ZONE:
+ final int timePrecision = getPrecision(type);
+ if (timePrecision < MIN_TIME_PRECISION || timePrecision > MAX_TIME_PRECISION) {
+ throw new UnsupportedOperationException(
+ String.format("The precision %s of TIME type is out of the range [%s, %s] supported by " +
+ "HBase connector", timePrecision, MIN_TIME_PRECISION, MAX_TIME_PRECISION));
+ }
+ return true;
case TIMESTAMP_WITHOUT_TIME_ZONE:
case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
- return getPrecision(type) <= 3;
+ final int timestampPrecision = getPrecision(type);
+ if (timestampPrecision < MIN_TIMESTAMP_PRECISION || timestampPrecision > MAX_TIMESTAMP_PRECISION) {
+ throw new UnsupportedOperationException(
+ String.format("The precision %s of TIMESTAMP type is out of the range [%s, %s] supported by " +
+ "HBase connector", timestampPrecision, MIN_TIMESTAMP_PRECISION, MAX_TIMESTAMP_PRECISION));
+ }
+ return true;
case TIMESTAMP_WITH_TIME_ZONE:
case ARRAY:
case MULTISET:
diff --git a/flink-connectors/flink-connector-hbase/src/test/java/org/apache/flink/connector/hbase/HBaseConnectorITCase.java b/flink-connectors/flink-connector-hbase/src/test/java/org/apache/flink/connector/hbase/HBaseConnectorITCase.java
index f04d5fd6cb2a88..9ab36cce280a2e 100644
--- a/flink-connectors/flink-connector-hbase/src/test/java/org/apache/flink/connector/hbase/HBaseConnectorITCase.java
+++ b/flink-connectors/flink-connector-hbase/src/test/java/org/apache/flink/connector/hbase/HBaseConnectorITCase.java
@@ -27,7 +27,6 @@
import org.apache.flink.api.java.typeutils.RowTypeInfo;
import org.apache.flink.connector.hbase.source.HBaseInputFormat;
import org.apache.flink.connector.hbase.source.HBaseTableSource;
-import org.apache.flink.connector.hbase.util.HBaseTableSchema;
import org.apache.flink.connector.hbase.util.HBaseTestBase;
import org.apache.flink.connector.hbase.util.PlannerType;
import org.apache.flink.streaming.api.datastream.DataStream;
@@ -36,26 +35,21 @@
import org.apache.flink.table.api.TableConfig;
import org.apache.flink.table.api.TableEnvironment;
import org.apache.flink.table.api.TableSchema;
+import org.apache.flink.table.api.bridge.java.BatchTableEnvironment;
+import org.apache.flink.table.api.bridge.java.StreamTableEnvironment;
import org.apache.flink.table.api.internal.TableEnvironmentInternal;
import org.apache.flink.table.api.internal.TableImpl;
-import org.apache.flink.table.api.java.BatchTableEnvironment;
-import org.apache.flink.table.api.java.StreamTableEnvironment;
-import org.apache.flink.table.descriptors.DescriptorProperties;
-import org.apache.flink.table.factories.TableFactoryService;
import org.apache.flink.table.functions.ScalarFunction;
import org.apache.flink.table.planner.runtime.utils.BatchTableEnvUtil;
import org.apache.flink.table.planner.runtime.utils.TableEnvUtil;
import org.apache.flink.table.planner.sinks.CollectRowTableSink;
import org.apache.flink.table.planner.sinks.CollectTableSink;
import org.apache.flink.table.planner.utils.JavaScalaConversionUtil;
-import org.apache.flink.table.runtime.utils.StreamITCase;
-import org.apache.flink.table.sinks.TableSink;
-import org.apache.flink.table.sources.TableSource;
import org.apache.flink.test.util.TestBaseUtils;
import org.apache.flink.types.Row;
-import org.apache.hadoop.hbase.HBaseConfiguration;
-import org.apache.hadoop.hbase.HConstants;
+import org.apache.flink.shaded.guava18.com.google.common.collect.Lists;
+
import org.apache.hadoop.hbase.client.Result;
import org.apache.hadoop.hbase.client.Scan;
import org.apache.hadoop.hbase.util.Bytes;
@@ -63,26 +57,15 @@
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
-import java.sql.Date;
-import java.sql.Time;
-import java.sql.Timestamp;
import java.util.ArrayList;
-import java.util.HashMap;
+import java.util.Iterator;
import java.util.List;
-import java.util.Map;
+import java.util.stream.Collectors;
import scala.Option;
import static org.apache.flink.connector.hbase.util.PlannerType.OLD_PLANNER;
import static org.apache.flink.table.api.Expressions.$;
-import static org.apache.flink.table.descriptors.ConnectorDescriptorValidator.CONNECTOR_PROPERTY_VERSION;
-import static org.apache.flink.table.descriptors.ConnectorDescriptorValidator.CONNECTOR_TYPE;
-import static org.apache.flink.table.descriptors.ConnectorDescriptorValidator.CONNECTOR_VERSION;
-import static org.apache.flink.table.descriptors.HBaseValidator.CONNECTOR_TABLE_NAME;
-import static org.apache.flink.table.descriptors.HBaseValidator.CONNECTOR_TYPE_VALUE_HBASE;
-import static org.apache.flink.table.descriptors.HBaseValidator.CONNECTOR_VERSION_VALUE_143;
-import static org.apache.flink.table.descriptors.HBaseValidator.CONNECTOR_ZK_QUORUM;
-import static org.apache.flink.table.descriptors.Schema.SCHEMA;
import static org.junit.Assert.assertEquals;
/**
@@ -126,7 +109,7 @@ public void testTableSourceFullScan() throws Exception {
hbaseTable.addColumn(FAMILY3, F3COL1, Double.class);
hbaseTable.addColumn(FAMILY3, F3COL2, Boolean.class);
hbaseTable.addColumn(FAMILY3, F3COL3, String.class);
- hbaseTable.setRowKey("rowkey", Integer.class);
+ hbaseTable.setRowKey(ROW_KEY, Integer.class);
((TableEnvironmentInternal) tEnv).registerTableSourceInternal("hTable", hbaseTable);
} else {
tEnv.executeSql(
@@ -178,7 +161,7 @@ public void testTableSourceProjection() throws Exception {
hbaseTable.addColumn(FAMILY3, F3COL1, Double.class);
hbaseTable.addColumn(FAMILY3, F3COL2, Boolean.class);
hbaseTable.addColumn(FAMILY3, F3COL3, String.class);
- hbaseTable.setRowKey("rowkey", Integer.class);
+ hbaseTable.setRowKey(ROW_KEY, Integer.class);
((TableEnvironmentInternal) tEnv).registerTableSourceInternal("hTable", hbaseTable);
} else {
tEnv.executeSql(
@@ -223,7 +206,7 @@ public void testTableSourceFieldOrder() throws Exception {
if (isLegacyConnector) {
HBaseTableSource hbaseTable = new HBaseTableSource(getConf(), TEST_TABLE_1);
// shuffle order of column registration
- hbaseTable.setRowKey("rowkey", Integer.class);
+ hbaseTable.setRowKey(ROW_KEY, Integer.class);
hbaseTable.addColumn(FAMILY2, F2COL1, String.class);
hbaseTable.addColumn(FAMILY3, F3COL1, Double.class);
hbaseTable.addColumn(FAMILY1, F1COL1, Integer.class);
@@ -270,7 +253,7 @@ public void testTableSourceReadAsByteArray() throws Exception {
HBaseTableSource hbaseTable = new HBaseTableSource(getConf(), TEST_TABLE_1);
hbaseTable.addColumn(FAMILY2, F2COL1, byte[].class);
hbaseTable.addColumn(FAMILY2, F2COL2, byte[].class);
- hbaseTable.setRowKey("rowkey", Integer.class);
+ hbaseTable.setRowKey(ROW_KEY, Integer.class);
((TableEnvironmentInternal) tEnv).registerTableSourceInternal("hTable", hbaseTable);
} else {
tEnv.executeSql(
@@ -312,7 +295,7 @@ public void testTableInputFormat() throws Exception {
ExecutionEnvironment env = ExecutionEnvironment.getExecutionEnvironment();
DataSet> result = env
- .createInput(new InputFormatForTestTable())
+ .createInput(new InputFormatForTestTable(getConf()))
.reduce((ReduceFunction>) (v1, v2) -> Tuple1.of(v1.f0 + v2.f0));
List> resultSet = result.collect();
@@ -321,115 +304,33 @@ public void testTableInputFormat() throws Exception {
assertEquals(360, (int) resultSet.get(0).f0);
}
- // -------------------------------------------------------------------------------------
- // HBaseTableSink tests
- // -------------------------------------------------------------------------------------
-
- // prepare a source collection.
- private static final List testData1 = new ArrayList<>();
- private static final RowTypeInfo testTypeInfo1 = new RowTypeInfo(
- new TypeInformation[]{Types.INT, Types.INT, Types.STRING, Types.LONG, Types.DOUBLE,
- Types.BOOLEAN, Types.STRING, Types.SQL_TIMESTAMP, Types.SQL_DATE, Types.SQL_TIME},
- new String[]{"rowkey", "f1c1", "f2c1", "f2c2", "f3c1", "f3c2", "f3c3", "f4c1", "f4c2", "f4c3"});
-
- static {
- testData1.add(Row.of(1, 10, "Hello-1", 100L, 1.01, false, "Welt-1",
- Timestamp.valueOf("2019-08-18 19:00:00"), Date.valueOf("2019-08-18"), Time.valueOf("19:00:00")));
- testData1.add(Row.of(2, 20, "Hello-2", 200L, 2.02, true, "Welt-2",
- Timestamp.valueOf("2019-08-18 19:01:00"), Date.valueOf("2019-08-18"), Time.valueOf("19:01:00")));
- testData1.add(Row.of(3, 30, "Hello-3", 300L, 3.03, false, "Welt-3",
- Timestamp.valueOf("2019-08-18 19:02:00"), Date.valueOf("2019-08-18"), Time.valueOf("19:02:00")));
- testData1.add(Row.of(4, 40, null, 400L, 4.04, true, "Welt-4",
- Timestamp.valueOf("2019-08-18 19:03:00"), Date.valueOf("2019-08-18"), Time.valueOf("19:03:00")));
- testData1.add(Row.of(5, 50, "Hello-5", 500L, 5.05, false, "Welt-5",
- Timestamp.valueOf("2019-08-19 19:10:00"), Date.valueOf("2019-08-19"), Time.valueOf("19:10:00")));
- testData1.add(Row.of(6, 60, "Hello-6", 600L, 6.06, true, "Welt-6",
- Timestamp.valueOf("2019-08-19 19:20:00"), Date.valueOf("2019-08-19"), Time.valueOf("19:20:00")));
- testData1.add(Row.of(7, 70, "Hello-7", 700L, 7.07, false, "Welt-7",
- Timestamp.valueOf("2019-08-19 19:30:00"), Date.valueOf("2019-08-19"), Time.valueOf("19:30:00")));
- testData1.add(Row.of(8, 80, null, 800L, 8.08, true, "Welt-8",
- Timestamp.valueOf("2019-08-19 19:40:00"), Date.valueOf("2019-08-19"), Time.valueOf("19:40:00")));
- }
-
@Test
public void testTableSink() throws Exception {
StreamExecutionEnvironment execEnv = StreamExecutionEnvironment.getExecutionEnvironment();
StreamTableEnvironment tEnv = StreamTableEnvironment.create(execEnv, streamSettings);
- if (isLegacyConnector) {
- HBaseTableSchema schema = new HBaseTableSchema();
- schema.addColumn(FAMILY1, F1COL1, Integer.class);
- schema.addColumn(FAMILY2, F2COL1, String.class);
- schema.addColumn(FAMILY2, F2COL2, Long.class);
- schema.setRowKey("rk", Integer.class);
- schema.addColumn(FAMILY3, F3COL1, Double.class);
- schema.addColumn(FAMILY3, F3COL2, Boolean.class);
- schema.addColumn(FAMILY3, F3COL3, String.class);
-
- Map tableProperties = new HashMap<>();
- tableProperties.put("connector.type", "hbase");
- tableProperties.put("connector.version", "1.4.3");
- tableProperties.put("connector.property-version", "1");
- tableProperties.put("connector.table-name", TEST_TABLE_2);
- tableProperties.put("connector.zookeeper.quorum", getZookeeperQuorum());
- tableProperties.put("connector.zookeeper.znode.parent", "/hbase");
- DescriptorProperties descriptorProperties = new DescriptorProperties(true);
- descriptorProperties.putTableSchema(SCHEMA, schema.convertsToTableSchema());
- descriptorProperties.putProperties(tableProperties);
- TableSink tableSink = TableFactoryService
- .find(HBaseTableFactory.class, descriptorProperties.asMap())
- .createTableSink(descriptorProperties.asMap());
- ((TableEnvironmentInternal) tEnv).registerTableSinkInternal("hbase", tableSink);
- } else {
- tEnv.executeSql(
- "CREATE TABLE hbase (" +
- " family1 ROW," +
- " family2 ROW," +
- " rk INT," +
- " family3 ROW" +
- ") WITH (" +
- " 'connector' = 'hbase-1.4'," +
- " 'table-name' = '" + TEST_TABLE_1 + "'," +
- " 'zookeeper.quorum' = '" + getZookeeperQuorum() + "'," +
- " 'zookeeper.znode-parent' = '/hbase'" +
- ")");
- }
+ // register HBase table testTable1 which contains test data
+ String table1DDL = createHBaseTableDDL(TEST_TABLE_1, false);
+ tEnv.executeSql(table1DDL);
+
+ String table2DDL = createHBaseTableDDL(TEST_TABLE_2, false);
+ tEnv.executeSql(table2DDL);
- DataStream ds = execEnv.fromCollection(testData1).returns(testTypeInfo1);
- tEnv.createTemporaryView("src", ds);
+ String query = "INSERT INTO " + TEST_TABLE_2 + " SELECT" +
+ " rowkey," +
+ " family1," +
+ " family2," +
+ " family3" +
+ " FROM " + TEST_TABLE_1;
- String query = "INSERT INTO hbase SELECT ROW(f1c1), ROW(f2c1, f2c2), rowkey, ROW(f3c1, f3c2, f3c3) FROM src";
+ // wait to finish
TableEnvUtil.execInsertSqlAndWaitResult(tEnv, query);
// start a batch scan job to verify contents in HBase table
- // start a batch scan job to verify contents in HBase table
- TableEnvironment batchTableEnv = createBatchTableEnv();
+ TableEnvironment batchEnv = createBatchTableEnv();
+ batchEnv.executeSql(table2DDL);
- if (isLegacyConnector) {
- HBaseTableSource hbaseTable = new HBaseTableSource(getConf(), TEST_TABLE_2);
- hbaseTable.setRowKey("rowkey", Integer.class);
- hbaseTable.addColumn(FAMILY1, F1COL1, Integer.class);
- hbaseTable.addColumn(FAMILY2, F2COL1, String.class);
- hbaseTable.addColumn(FAMILY2, F2COL2, Long.class);
- hbaseTable.addColumn(FAMILY3, F3COL1, Double.class);
- hbaseTable.addColumn(FAMILY3, F3COL2, Boolean.class);
- hbaseTable.addColumn(FAMILY3, F3COL3, String.class);
- ((TableEnvironmentInternal) batchTableEnv).registerTableSourceInternal("hTable", hbaseTable);
- } else {
- batchTableEnv.executeSql(
- "CREATE TABLE hTable (" +
- " rowkey INT," +
- " family1 ROW," +
- " family2 ROW," +
- " family3 ROW" +
- ") WITH (" +
- " 'connector' = 'hbase-1.4'," +
- " 'table-name' = '" + TEST_TABLE_1 + "'," +
- " 'zookeeper.quorum' = '" + getZookeeperQuorum() + "'" +
- ")");
- }
-
- Table table = batchTableEnv.sqlQuery(
+ Table table = batchEnv.sqlQuery(
"SELECT " +
" h.rowkey, " +
" h.family1.col1, " +
@@ -438,9 +339,8 @@ public void testTableSink() throws Exception {
" h.family3.col1, " +
" h.family3.col2, " +
" h.family3.col3 " +
- "FROM hTable AS h"
+ "FROM " + TEST_TABLE_2 + " AS h"
);
-
List results = collectBatchResult(table);
String expected =
"1,10,Hello-1,100,1.01,false,Welt-1\n" +
@@ -457,56 +357,37 @@ public void testTableSink() throws Exception {
@Test
public void testTableSourceSinkWithDDL() throws Exception {
+ if (OLD_PLANNER.equals(planner) || isLegacyConnector) {
+ // only test for blink planner and new connector, because types TIMESTAMP/DATE/TIME/DECIMAL works well in
+ // new connector(using blink-planner), but exits some precision problem in old planner or legacy connector.
+ return;
+ }
+
StreamExecutionEnvironment execEnv = StreamExecutionEnvironment.getExecutionEnvironment();
StreamTableEnvironment tEnv = StreamTableEnvironment.create(execEnv, streamSettings);
- DataStream ds = execEnv.fromCollection(testData1).returns(testTypeInfo1);
- tEnv.createTemporaryView("src", ds);
+ // regiter HBase table testTable1 which contains test data
+ String table1DDL = createHBaseTableDDL(TEST_TABLE_1, true);
+ tEnv.executeSql(table1DDL);
- // register hbase table
- String quorum = getZookeeperQuorum();
- String ddl;
- if (isLegacyConnector) {
- ddl = "CREATE TABLE hbase (\n" +
- " rowkey INT," +
- " family1 ROW,\n" +
- " family2 ROW,\n" +
- " family3 ROW,\n" +
- " family4 ROW\n" +
- ") WITH (\n" +
- " 'connector.type' = 'hbase',\n" +
- " 'connector.version' = '1.4.3',\n" +
- " 'connector.table-name' = 'testTable3',\n" +
- " 'connector.zookeeper.quorum' = '" + quorum + "',\n" +
- " 'connector.zookeeper.znode.parent' = '/hbase' " +
- ")";
- } else {
- ddl = "CREATE TABLE hbase (\n" +
- " rowkey INT," +
- " family1 ROW,\n" +
- " family2 ROW,\n" +
- " family3 ROW,\n" +
- " family4 ROW\n" +
- ") WITH (\n" +
- " 'connector' = 'hbase-1.4',\n" +
- " 'table-name' = 'testTable3',\n" +
- " 'zookeeper.quorum' = '" + quorum + "',\n" +
- " 'zookeeper.znode-parent' = '/hbase' " +
- ")";
- }
- tEnv.executeSql(ddl);
+ // register HBase table which is empty
+ String table3DDL = createHBaseTableDDL(TEST_TABLE_3, true);
+ tEnv.executeSql(table3DDL);
- String query = "INSERT INTO hbase " +
- "SELECT rowkey, ROW(f1c1), ROW(f2c1, f2c2), ROW(f3c1, f3c2, f3c3), ROW(f4c1, f4c2, f4c3) " +
- "FROM src";
- TableEnvUtil.execInsertSqlAndWaitResult(tEnv, query);
+ String insertStatement = "INSERT INTO " + TEST_TABLE_3 +
+ " SELECT rowkey," +
+ " family1," +
+ " family2," +
+ " family3," +
+ " family4" +
+ " from " + TEST_TABLE_1;
+ // wait to finish
+ TableEnvUtil.execInsertSqlAndWaitResult(tEnv, insertStatement);
// start a batch scan job to verify contents in HBase table
- TableEnvironment batchTableEnv = createBatchTableEnv();
- batchTableEnv.executeSql(ddl);
-
- Table table = batchTableEnv.sqlQuery(
- "SELECT " +
+ TableEnvironment batchEnv = createBatchTableEnv();
+ batchEnv.executeSql(table3DDL);
+ String query = "SELECT " +
" h.rowkey, " +
" h.family1.col1, " +
" h.family2.col1, " +
@@ -516,117 +397,103 @@ public void testTableSourceSinkWithDDL() throws Exception {
" h.family3.col3, " +
" h.family4.col1, " +
" h.family4.col2, " +
- " h.family4.col3 " +
- "FROM hbase AS h"
- );
-
- List results = collectBatchResult(table);
- String expected =
- "1,10,Hello-1,100,1.01,false,Welt-1,2019-08-18 19:00:00.0,2019-08-18,19:00:00\n" +
- "2,20,Hello-2,200,2.02,true,Welt-2,2019-08-18 19:01:00.0,2019-08-18,19:01:00\n" +
- "3,30,Hello-3,300,3.03,false,Welt-3,2019-08-18 19:02:00.0,2019-08-18,19:02:00\n" +
- "4,40,null,400,4.04,true,Welt-4,2019-08-18 19:03:00.0,2019-08-18,19:03:00\n" +
- "5,50,Hello-5,500,5.05,false,Welt-5,2019-08-19 19:10:00.0,2019-08-19,19:10:00\n" +
- "6,60,Hello-6,600,6.06,true,Welt-6,2019-08-19 19:20:00.0,2019-08-19,19:20:00\n" +
- "7,70,Hello-7,700,7.07,false,Welt-7,2019-08-19 19:30:00.0,2019-08-19,19:30:00\n" +
- "8,80,null,800,8.08,true,Welt-8,2019-08-19 19:40:00.0,2019-08-19,19:40:00\n";
-
- TestBaseUtils.compareResultAsText(results, expected);
- }
-
-
- // -------------------------------------------------------------------------------------
- // HBase lookup source tests
- // -------------------------------------------------------------------------------------
-
- // prepare a source collection.
- private static final List testData2 = new ArrayList<>();
- private static final RowTypeInfo testTypeInfo2 = new RowTypeInfo(
- new TypeInformation[]{Types.INT, Types.LONG, Types.STRING},
- new String[]{"a", "b", "c"});
+ " h.family4.col3, " +
+ " h.family4.col4 " +
+ " FROM " + TEST_TABLE_3 + " AS h";
+ Iterator collected = tEnv.executeSql(query).collect();
+ List result = Lists.newArrayList(collected).stream()
+ .map(Row::toString)
+ .sorted()
+ .collect(Collectors.toList());
- static {
- testData2.add(Row.of(1, 1L, "Hi"));
- testData2.add(Row.of(2, 2L, "Hello"));
- testData2.add(Row.of(3, 2L, "Hello world"));
- testData2.add(Row.of(3, 3L, "Hello world!"));
+ List expected = new ArrayList<>();
+ expected.add("1,10,Hello-1,100,1.01,false,Welt-1,2019-08-18T19:00,2019-08-18,19:00,12345678.0001");
+ expected.add("2,20,Hello-2,200,2.02,true,Welt-2,2019-08-18T19:01,2019-08-18,19:01,12345678.0002");
+ expected.add("3,30,Hello-3,300,3.03,false,Welt-3,2019-08-18T19:02,2019-08-18,19:02,12345678.0003");
+ expected.add("4,40,null,400,4.04,true,Welt-4,2019-08-18T19:03,2019-08-18,19:03,12345678.0004");
+ expected.add("5,50,Hello-5,500,5.05,false,Welt-5,2019-08-19T19:10,2019-08-19,19:10,12345678.0005");
+ expected.add("6,60,Hello-6,600,6.06,true,Welt-6,2019-08-19T19:20,2019-08-19,19:20,12345678.0006");
+ expected.add("7,70,Hello-7,700,7.07,false,Welt-7,2019-08-19T19:30,2019-08-19,19:30,12345678.0007");
+ expected.add("8,80,null,800,8.08,true,Welt-8,2019-08-19T19:40,2019-08-19,19:40,12345678.0008");
+ assertEquals(expected, result);
}
@Test
public void testHBaseLookupTableSource() throws Exception {
- if (OLD_PLANNER.equals(planner)) {
+ if (OLD_PLANNER.equals(planner) || isLegacyConnector) {
// lookup table source is only supported in blink planner, skip for old planner
+ // types TIMESTAMP/DATE/TIME/DECIMAL works well in new connector, skip legacy connector
return;
}
- StreamExecutionEnvironment streamEnv = StreamExecutionEnvironment.getExecutionEnvironment();
- StreamTableEnvironment streamTableEnv = StreamTableEnvironment.create(streamEnv, streamSettings);
- StreamITCase.clear();
+
+ StreamExecutionEnvironment execEnv = StreamExecutionEnvironment.getExecutionEnvironment();
+ StreamTableEnvironment tEnv = StreamTableEnvironment.create(execEnv, streamSettings);
+
+ tEnv.executeSql(
+ "CREATE TABLE " + TEST_TABLE_1 + " (" +
+ " family1 ROW," +
+ " family2 ROW," +
+ " family3 ROW," +
+ " rowkey INT," +
+ " family4 ROW," +
+ " PRIMARY KEY (rowkey) NOT ENFORCED" +
+ ") WITH (" +
+ " 'connector' = 'hbase-1.4'," +
+ " 'table-name' = '" + TEST_TABLE_1 + "'," +
+ " 'zookeeper.quorum' = '" + getZookeeperQuorum() + "'" +
+ ")");
// prepare a source table
String srcTableName = "src";
- DataStream ds = streamEnv.fromCollection(testData2).returns(testTypeInfo2);
- Table in = streamTableEnv.fromDataStream(ds, $("a"), $("b"), $("c"), $("proc").proctime());
- streamTableEnv.registerTable(srcTableName, in);
+ DataStream srcDs = execEnv.fromCollection(testData).returns(testTypeInfo);
+ Table in = tEnv.fromDataStream(srcDs, $("a"), $("b"), $("c"), $("proc").proctime());
+ tEnv.registerTable(srcTableName, in);
- if (isLegacyConnector) {
- Map tableProperties = hbaseTableProperties();
- TableSource> source = TableFactoryService
- .find(HBaseTableFactory.class, tableProperties)
- .createTableSource(tableProperties);
- ((TableEnvironmentInternal) streamTableEnv).registerTableSourceInternal("hbaseLookup", source);
- } else {
- streamTableEnv.executeSql(
- "CREATE TABLE hbaseLookup (" +
- " family1 ROW," +
- " rk INT," +
- " family2 ROW," +
- " family3 ROW" +
- ") WITH (" +
- " 'connector' = 'hbase-1.4'," +
- " 'table-name' = '" + TEST_TABLE_1 + "'," +
- " 'zookeeper.quorum' = '" + getZookeeperQuorum() + "'" +
- ")");
- }
// perform a temporal table join query
- String query = "SELECT a,family1.col1, family3.col3 FROM src " +
- "JOIN hbaseLookup FOR SYSTEM_TIME AS OF src.proc as h ON src.a = h.rk";
- Table result = streamTableEnv.sqlQuery(query);
-
- DataStream resultSet = streamTableEnv.toAppendStream(result, Row.class);
- resultSet.addSink(new StreamITCase.StringSink<>());
-
- streamEnv.execute();
+ String dimJoinQuery = "SELECT" +
+ " a," +
+ " b," +
+ " h.family1.col1," +
+ " h.family2.col1," +
+ " h.family2.col2," +
+ " h.family3.col1," +
+ " h.family3.col2," +
+ " h.family3.col3," +
+ " h.family4.col1," +
+ " h.family4.col2," +
+ " h.family4.col3," +
+ " h.family4.col4 " +
+ " FROM src JOIN " + TEST_TABLE_1 + " FOR SYSTEM_TIME AS OF src.proc as h ON src.a = h.rowkey";
+ Iterator collected = tEnv.executeSql(dimJoinQuery).collect();
+ List result = Lists.newArrayList(collected).stream()
+ .map(Row::toString)
+ .sorted()
+ .collect(Collectors.toList());
List expected = new ArrayList<>();
- expected.add("1,10,Welt-1");
- expected.add("2,20,Welt-2");
- expected.add("3,30,Welt-3");
- expected.add("3,30,Welt-3");
+ expected.add("1,1,10,Hello-1,100,1.01,false,Welt-1,2019-08-18T19:00,2019-08-18,19:00,12345678.0001");
+ expected.add("2,2,20,Hello-2,200,2.02,true,Welt-2,2019-08-18T19:01,2019-08-18,19:01,12345678.0002");
+ expected.add("3,2,30,Hello-3,300,3.03,false,Welt-3,2019-08-18T19:02,2019-08-18,19:02,12345678.0003");
+ expected.add("3,3,30,Hello-3,300,3.03,false,Welt-3,2019-08-18T19:02,2019-08-18,19:02,12345678.0003");
- StreamITCase.compareWithList(expected);
+ assertEquals(expected, result);
}
- private static Map hbaseTableProperties() {
- Map properties = new HashMap<>();
- properties.put(CONNECTOR_TYPE, CONNECTOR_TYPE_VALUE_HBASE);
- properties.put(CONNECTOR_VERSION, CONNECTOR_VERSION_VALUE_143);
- properties.put(CONNECTOR_PROPERTY_VERSION, "1");
- properties.put(CONNECTOR_TABLE_NAME, TEST_TABLE_1);
- // get zk quorum from "hbase-site.xml" in classpath
- String hbaseZk = HBaseConfiguration.create().get(HConstants.ZOOKEEPER_QUORUM);
- properties.put(CONNECTOR_ZK_QUORUM, hbaseZk);
- // schema
- String[] columnNames = {FAMILY1, ROWKEY, FAMILY2, FAMILY3};
- TypeInformation f1 = Types.ROW_NAMED(new String[]{F1COL1}, Types.INT);
- TypeInformation f2 = Types.ROW_NAMED(new String[]{F2COL1, F2COL2}, Types.STRING, Types.LONG);
- TypeInformation f3 = Types.ROW_NAMED(new String[]{F3COL1, F3COL2, F3COL3}, Types.DOUBLE, Types.BOOLEAN, Types.STRING);
- TypeInformation[] columnTypes = new TypeInformation[]{f1, Types.INT, f2, f3};
-
- DescriptorProperties descriptorProperties = new DescriptorProperties(true);
- TableSchema tableSchema = new TableSchema(columnNames, columnTypes);
- descriptorProperties.putTableSchema(SCHEMA, tableSchema);
- descriptorProperties.putProperties(properties);
- return descriptorProperties.asMap();
+ // -------------------------------------------------------------------------------------
+ // HBase lookup source tests
+ // -------------------------------------------------------------------------------------
+
+ // prepare a source collection.
+ private static final List testData = new ArrayList<>();
+ private static final RowTypeInfo testTypeInfo = new RowTypeInfo(
+ new TypeInformation[]{Types.INT, Types.LONG, Types.STRING},
+ new String[]{"a", "b", "c"});
+
+ static {
+ testData.add(Row.of(1, 1L, "Hi"));
+ testData.add(Row.of(2, 2L, "Hello"));
+ testData.add(Row.of(3, 2L, "Hello world"));
+ testData.add(Row.of(3, 3L, "Hello world!"));
}
// ------------------------------- Utilities -------------------------------------------------
@@ -705,6 +572,10 @@ public long eval(byte[] bytes) {
public static class InputFormatForTestTable extends HBaseInputFormat> {
private static final long serialVersionUID = 1L;
+ public InputFormatForTestTable(org.apache.hadoop.conf.Configuration hConf) {
+ super(hConf);
+ }
+
@Override
protected Scan getScanner() {
return new Scan();
@@ -721,4 +592,42 @@ protected Tuple1 mapResultToTuple(Result r) {
}
}
+ private String createHBaseTableDDL(String tableName, boolean testTimeAndDecimalTypes) {
+ StringBuilder family4Statement = new StringBuilder();
+ if (testTimeAndDecimalTypes) {
+ family4Statement.append(", family4 ROW \n");
+ }
+ if (isLegacyConnector) {
+ return "CREATE TABLE " + tableName + "(\n" +
+ " rowkey INT,\n" +
+ " family1 ROW,\n" +
+ " family2 ROW,\n" +
+ " family3 ROW" +
+ family4Statement.toString() +
+ ") WITH (\n" +
+ " 'connector.type' = 'hbase',\n" +
+ " 'connector.version' = '1.4.3',\n" +
+ " 'connector.table-name' = '" + tableName + "',\n" +
+ " 'connector.zookeeper.quorum' = '" + getZookeeperQuorum() + "',\n" +
+ " 'connector.zookeeper.znode.parent' = '/hbase' " +
+ ")";
+ } else {
+ return "CREATE TABLE " + tableName + "(\n" +
+ " rowkey INT," +
+ " family1 ROW,\n" +
+ " family2 ROW,\n" +
+ " family3 ROW" +
+ family4Statement.toString() +
+ ") WITH (\n" +
+ " 'connector' = 'hbase-1.4',\n" +
+ " 'table-name' = '" + tableName + "',\n" +
+ " 'zookeeper.quorum' = '" + getZookeeperQuorum() + "',\n" +
+ " 'zookeeper.znode.parent' = '/hbase' " +
+ ")";
+ }
+ }
}
diff --git a/flink-connectors/flink-connector-hbase/src/test/java/org/apache/flink/connector/hbase/HBaseDynamicTableFactoryTest.java b/flink-connectors/flink-connector-hbase/src/test/java/org/apache/flink/connector/hbase/HBaseDynamicTableFactoryTest.java
index 17fe3a650c7e9f..47775ac2b4293d 100644
--- a/flink-connectors/flink-connector-hbase/src/test/java/org/apache/flink/connector/hbase/HBaseDynamicTableFactoryTest.java
+++ b/flink-connectors/flink-connector-hbase/src/test/java/org/apache/flink/connector/hbase/HBaseDynamicTableFactoryTest.java
@@ -24,7 +24,7 @@
import org.apache.flink.connector.hbase.options.HBaseWriteOptions;
import org.apache.flink.connector.hbase.sink.HBaseDynamicTableSink;
import org.apache.flink.connector.hbase.source.HBaseDynamicTableSource;
-import org.apache.flink.connector.hbase.source.HBaseLookupFunction;
+import org.apache.flink.connector.hbase.source.HBaseRowDataLookupFunction;
import org.apache.flink.connector.hbase.util.HBaseTableSchema;
import org.apache.flink.table.api.TableSchema;
import org.apache.flink.table.catalog.CatalogTableImpl;
@@ -111,8 +111,8 @@ public void testTableSourceFactory() {
assertTrue(lookupProvider instanceof TableFunctionProvider);
TableFunction tableFunction = ((TableFunctionProvider) lookupProvider).createTableFunction();
- assertTrue(tableFunction instanceof HBaseLookupFunction);
- assertEquals("testHBastTable", ((HBaseLookupFunction) tableFunction).getHTableName());
+ assertTrue(tableFunction instanceof HBaseRowDataLookupFunction);
+ assertEquals("testHBastTable", ((HBaseRowDataLookupFunction) tableFunction).getHTableName());
HBaseTableSchema hbaseSchema = hbaseSource.getHBaseTableSchema();
assertEquals(2, hbaseSchema.getRowKeyIndex());
@@ -182,13 +182,55 @@ public void testTableSinkFactory() {
HBaseWriteOptions expectedWriteOptions = HBaseWriteOptions.builder()
.setBufferFlushMaxRows(1000)
- .setBufferFlushIntervalMillis(10 * 1000)
- .setBufferFlushMaxSizeInBytes(10 * 1024 * 1024)
+ .setBufferFlushIntervalMillis(1000)
+ .setBufferFlushMaxSizeInBytes(2 * 1024 * 1024)
.build();
HBaseWriteOptions actualWriteOptions = hbaseSink.getWriteOptions();
assertEquals(expectedWriteOptions, actualWriteOptions);
}
+ @Test
+ public void testBufferFlushOptions() {
+ Map options = getAllOptions();
+ options.put("sink.buffer-flush.max-size", "10mb");
+ options.put("sink.buffer-flush.max-rows", "100");
+ options.put("sink.buffer-flush.interval", "10s");
+
+ TableSchema schema = TableSchema.builder()
+ .field(ROWKEY, STRING())
+ .build();
+
+ DynamicTableSink sink = createTableSink(schema, options);
+ HBaseWriteOptions expected = HBaseWriteOptions.builder()
+ .setBufferFlushMaxRows(100)
+ .setBufferFlushIntervalMillis(10 * 1000)
+ .setBufferFlushMaxSizeInBytes(10 * 1024 * 1024)
+ .build();
+ HBaseWriteOptions actual = ((HBaseDynamicTableSink) sink).getWriteOptions();
+ assertEquals(expected, actual);
+ }
+
+ @Test
+ public void testDisabledBufferFlushOptions() {
+ Map options = getAllOptions();
+ options.put("sink.buffer-flush.max-size", "0");
+ options.put("sink.buffer-flush.max-rows", "0");
+ options.put("sink.buffer-flush.interval", "0");
+
+ TableSchema schema = TableSchema.builder()
+ .field(ROWKEY, STRING())
+ .build();
+
+ DynamicTableSink sink = createTableSink(schema, options);
+ HBaseWriteOptions expected = HBaseWriteOptions.builder()
+ .setBufferFlushMaxRows(0)
+ .setBufferFlushIntervalMillis(0)
+ .setBufferFlushMaxSizeInBytes(0)
+ .build();
+ HBaseWriteOptions actual = ((HBaseDynamicTableSink) sink).getWriteOptions();
+ assertEquals(expected, actual);
+ }
+
@Test
public void testUnknownOption() {
Map options = getAllOptions();
@@ -219,15 +261,70 @@ public void testUnknownOption() {
}
}
+ @Test
+ public void testTypeWithUnsupportedPrecision() {
+ Map options = getAllOptions();
+ // test unsupported timestamp precision
+ TableSchema schema = TableSchema.builder()
+ .field(ROWKEY, STRING())
+ .field(FAMILY1, ROW(
+ FIELD(COL1, TIMESTAMP(6)),
+ FIELD(COL2, INT())))
+ .build();
+ try {
+ createTableSource(schema, options);
+ fail("Should fail");
+ } catch (Exception e) {
+ assertTrue(ExceptionUtils
+ .findThrowableWithMessage(e, "The precision 6 of TIMESTAMP type is out of the range [0, 3]" +
+ " supported by HBase connector")
+ .isPresent());
+ }
+
+ try {
+ createTableSink(schema, options);
+ fail("Should fail");
+ } catch (Exception e) {
+ assertTrue(ExceptionUtils
+ .findThrowableWithMessage(e, "The precision 6 of TIMESTAMP type is out of the range [0, 3]" +
+ " supported by HBase connector")
+ .isPresent());
+ }
+ // test unsupported time precision
+ schema = TableSchema.builder()
+ .field(ROWKEY, STRING())
+ .field(FAMILY1, ROW(
+ FIELD(COL1, TIME(6)),
+ FIELD(COL2, INT())))
+ .build();
+
+ try {
+ createTableSource(schema, options);
+ fail("Should fail");
+ } catch (Exception e) {
+ assertTrue(ExceptionUtils
+ .findThrowableWithMessage(e, "The precision 6 of TIME type is out of the range [0, 3]" +
+ " supported by HBase connector")
+ .isPresent());
+ }
+
+ try {
+ createTableSink(schema, options);
+ fail("Should fail");
+ } catch (Exception e) {
+ assertTrue(ExceptionUtils
+ .findThrowableWithMessage(e, "The precision 6 of TIME type is out of the range [0, 3]" +
+ " supported by HBase connector")
+ .isPresent());
+ }
+ }
+
private Map getAllOptions() {
Map options = new HashMap<>();
options.put("connector", "hbase-1.4");
options.put("table-name", "testHBastTable");
options.put("zookeeper.quorum", "localhost:2181");
- options.put("zookeeper.znode-parent", "/flink");
- options.put("sink.buffer-flush.max-size", "10mb");
- options.put("sink.buffer-flush.max-rows", "1000");
- options.put("sink.buffer-flush.interval", "10s");
+ options.put("zookeeper.znode.parent", "/flink");
return options;
}
diff --git a/flink-connectors/flink-connector-hbase/src/test/java/org/apache/flink/connector/hbase/HBaseTablePlanTest.java b/flink-connectors/flink-connector-hbase/src/test/java/org/apache/flink/connector/hbase/HBaseTablePlanTest.java
new file mode 100644
index 00000000000000..053cf9982413e3
--- /dev/null
+++ b/flink-connectors/flink-connector-hbase/src/test/java/org/apache/flink/connector/hbase/HBaseTablePlanTest.java
@@ -0,0 +1,127 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.flink.connector.hbase;
+
+import org.apache.flink.table.api.TableConfig;
+import org.apache.flink.table.planner.utils.StreamTableTestUtil;
+import org.apache.flink.table.planner.utils.TableTestBase;
+
+import org.junit.Test;
+
+import static org.apache.flink.util.CoreMatchers.containsCause;
+
+/**
+ * Plan tests for HBase connector, for example, testing projection push down.
+ */
+public class HBaseTablePlanTest extends TableTestBase {
+
+ private final StreamTableTestUtil util = streamTestUtil(new TableConfig());
+
+ @Test
+ public void testMultipleRowKey() {
+ util.tableEnv().executeSql(
+ "CREATE TABLE hTable (" +
+ " family1 ROW," +
+ " family2 ROW," +
+ " rowkey INT," +
+ " rowkey2 STRING " +
+ ") WITH (" +
+ " 'connector' = 'hbase-1.4'," +
+ " 'table-name' = 'my_table'," +
+ " 'zookeeper.quorum' = 'localhost:2021'" +
+ ")");
+ thrown().expect(containsCause(new IllegalArgumentException("Row key can't be set multiple times.")));
+ util.verifyPlan("SELECT * FROM hTable");
+ }
+
+ @Test
+ public void testNoneRowKey() {
+ util.tableEnv().executeSql(
+ "CREATE TABLE hTable (" +
+ " family1 ROW," +
+ " family2 ROW