schongloo opened a new pull request, #1231:
URL: https://github.com/apache/flink-kubernetes-operator/pull/1231

   ## What is the purpose of the change
   
   This PR adds the `ADVANCED` transition mode to Blue/Green Deployments 
(FLIP-504), for review, without the automatic gate injection of #1043: the job 
places the gate in its own code. It is #1043 without the JVM agent and the 
StreamGraph rewriting, so it no longer relies on `@Internal` Flink APIs, and it 
adds about 3,200 fewer lines (5,359 instead of 8,571). The coordination, the 
teardown, the gate timeout and the aborts are the same code. Automatic 
injection could follow as its own PR on top of this one: it is the revert of 
this branch's last commit.
   
   The `BASIC` mode from FLIP-503 switches whole deployments: once the new 
deployment is stable the old one is retired, and while both run they write the 
same records. `ADVANCED` coordinates the two jobs record by record. A gate in 
both jobs decides which job writes each record from the record's event time and 
the job's watermark. Every record that arrives within the job's watermark is 
written exactly once across the transition. The old deployment is torn down 
only once all its gate subtasks have handed over, and with a savepoint, so 
sinks that commit on checkpoints commit what it wrote. The limits of that 
guarantee (late records right around the cutover, an abort after the cutover 
point) are listed under [Known limitations](#known-limitations).
   
   The mode is opt-in: `BASIC` stays the default, and existing Blue/Green 
deployments are unaffected.
   
   Besides the unit tests, an end-to-end test runs a transition of a job built 
for the purpose and checks that every record is written exactly once, against 
Flink 1.20, 2.0, 2.1 and 2.2, natively and standalone (see [Verifying this 
change](#verifying-this-change)). An abort after the cutover point is not 
covered end to end yet.
   
   Suggested reading order:
   1. The Advanced Transition Mode section of 
`docs/content/docs/managing/bluegreen-deployments.md`, for the behavior and its 
guarantees.
   2. The operator side: the Blue/Green state machine in 
`BlueGreenDeploymentService`, and `BlueGreenTransitionUtils`.
   3. The gate itself, in `flink-kubernetes-operator-bluegreen-client`: 
`GateProcessFunction` and `WatermarkGateProcessFunction`.
   
   ## Brief change log
   
   **API**
   - `FlinkBlueGreenDeployment` gains `spec.transitionMode` (`BASIC` / 
`ADVANCED`). `BASIC` is the default on both the CRD schema and the spec type, 
so deployments that omit it keep working. The field is immutable after the 
initial deployment: a change is rejected, the spec reverted and a Warning event 
emitted.
   
   **Operator**
   - In `ADVANCED` mode the operator keeps a ConfigMap per Blue/Green pair, 
through which it and the gates of both jobs coordinate. Writes are a 
compare-and-set on the resourceVersion: the first write wins, and writes for 
another transition are refused. Each side writes only its own turn: the active 
gate signals that it waits for the cutover point (the watermark toggle), and 
only the standby sets it, from its own watermark.
   - It sets `bluegreen.active-deployment-type` and `bluegreen.configmap.name` 
in the `flinkConfiguration` of both deployments, the keys the gate reads from 
the job's configuration.
   - The standby is torn down once every gate subtask has handed over, as 
reported by a `bluegreenGateHandOverDone` gauge read over the REST API. It is 
stopped with a savepoint, then deleted.
   - `kubernetes.operator.bluegreen.gate.timeout` (defaults to the abort grace 
period, and must be longer than the deployment deletion delay) bounds the wait 
for the gate: past it, the transition aborts. A job without a gate therefore 
aborts at the timeout. Every abort hands the gate back to the deployment that 
keeps running, and an abort after the cutover point redeploys it from the 
transition savepoint.
   - `ADVANCED` rejects the `STATELESS` upgrade mode. In either mode, a 
transition that changes the state backend is rejected while the savepoints are 
native, and a canonical savepoint that fails on Flink 2.x names the native 
format in the error.
   
   **Gate (`flink-kubernetes-operator-bluegreen-client`)**
   - `GateProcessFunction` defines the gate contract, and 
`WatermarkGateProcessFunction` implements it. A job creates it with 
`WatermarkGateProcessFunction.create(config, extractor)`, passing its 
configuration and a `WatermarkExtractor` that returns each record's event time. 
The job's watermark drives only the gate's state, and nothing falls back to the 
wall clock.
   - The active gate passes every record once its own watermark passes the 
toggle, so late records are not dropped after the hand-over.
   - The gate logs what each subtask does with its records once each time it 
changes, at INFO.
   - One client artifact serves Flink 1.x and 2.x jobs (see 
[Artifacts](#artifacts)). It brings fabric8's JDK HTTP client, and leaves its 
Flink dependencies to the Flink distribution.
   
   **Tests and docs**
   - End-to-end: `test_bluegreen_advanced.sh` with the new 
`examples/flink-bluegreen-advanced-example` job, and the stateless, last-state 
and ingress rotation Blue/Green tests, all in the CI matrix.
   - The client tests run against Flink 1.20, 2.0, 2.1 and 2.2 in CI.
   - User documentation, see [Documentation](#documentation).
   
   ## Using it
   
   ```yaml
   spec:
     transitionMode: ADVANCED
   ```
   
   ```java
   StreamExecutionEnvironment env = 
StreamExecutionEnvironment.getExecutionEnvironment();
   events.process(
                   WatermarkGateProcessFunction.create(
                           env.getConfiguration().toMap(), (MyEvent event) -> 
event.getEventTime()))
           .name("BlueGreen-Gate")
           .uid("bluegreen-gate")
           .sinkTo(sink);
   ```
   
   The gate goes in front of the operator that writes the records, for a sink 
with a committer its writer. With the same parallelism, Flink chains it to that 
operator: in the example job the chain is `BlueGreen-Gate -> Sink: Writer -> 
Sink: Committer`, on Flink 1.20 and 2.2 alike.
   
   <a name="hand-over"></a>
   ### Hand-over guarantees
   
   - Every record that arrives within the job's watermark is written exactly 
once: the standby writes records older than the toggle until its watermark 
passes it, the active the rest.
   - Late records are written by the standby before the crossing and by the 
active after it. Only late records arriving right around the crossing can be 
written twice or dropped, since the two jobs' watermarks do not advance in 
lockstep.
   - A record without a timestamp counts as older than the toggle.
   - An abort before the toggle is set needs nothing, since the standby still 
passed every record. After it, the survivor is redeployed from the transition 
savepoint, so records written since may be written twice, never lost.
   
   ## Requirements
   
   - **Java 17.** The gate client is Java 17 bytecode, like the rest of the 
operator, so a job on Java 11 fails with `UnsupportedClassVersionError` when it 
loads the gate. `BASIC` and non-Blue/Green deployments are not affected.
   - **A `DataStream` to place the gate on**, in the job's code.
   
   <a name="artifacts"></a>
   ## Artifacts
   
   The client is one artifact, 
`flink-kubernetes-operator-bluegreen-client:<version>`, for jobs on Flink 1.x 
and 2.x. Built against the operator's Flink version, every Flink class, method 
and field it references resolves with the same descriptor and kind in Flink 
1.19, 1.20, 2.0, 2.1 and 2.2, and the end-to-end jobs on Flink 2.x bundle it as 
built. Its Flink dependencies are `provided`, so it does not bring the 
operator's Flink version into the job.
   
   <a name="known-limitations"></a>
   ## Known limitations
   
   - `ADVANCED` needs a change to the job: the gate is placed in its code. SQL 
jobs that write through `INSERT INTO` have no `DataStream` to place it on.
   - `ADVANCED` needs event-time watermarks: without them there is no toggle, 
and the transition aborts after the gate timeout.
   - Late records arriving right around the crossing can be written twice or 
dropped, and an abort after the cutover point may write records twice.
   - A gate subtask that receives no records counts as done after 30 s without 
one.
   - On Flink 2.x, operators using the async state API cannot take a canonical 
savepoint, so a job using them needs 
`kubernetes.operator.savepoint.format.type: NATIVE` for every transition except 
a stateless one, in either mode, and cannot change its state backend through a 
transition.
   
   ## Verifying this change
   
   - `WatermarkGateProcessFunctionTest`: the hand-over between a standby and an 
active gate, late records, records without a timestamp, record time only, the 
turns each side writes, the logging and the hand-over gauge.
   - `GateKubernetesServiceTest`: the compare-and-set against a Kubernetes mock 
server.
   - `FlinkBlueGreenDeploymentControllerTest`: the `ADVANCED` state machine, 
from the ConfigMap and validation to the teardown, the gate timeout and the 
aborts, plus the savepoint format checks.
   - `BlueGreenTransitionUtilsTest`, `BlueGreenUtilsTest` and 
`FlinkBlueGreenDeploymentSpecDiffTest`: the validations and `transitionMode` 
immutability.
   
   `mvn verify` passes across all modules, except the three tests that need a 
local kube-apiserver. The client tests also pass against Flink 2.0.1, 2.1.1 and 
2.2.1. Built against Flink 1.20 and against 2.2, the example job's `main()` 
yields the chain `BlueGreen-Gate -> Sink: Writer -> Sink: Committer`, and its 
JobGraph builds, which serializes the gate with its extractor.
   
   TODO before leaving draft: link the run of the Blue/Green end-to-end jobs 
for this branch.
   
   ## Does this pull request potentially affect one of the following parts:
   
   - Dependencies (does it add or upgrade a dependency): **yes**. One new 
module, the client. It brings fabric8's `kubernetes-httpclient-jdk`, takes its 
Flink dependencies as `provided`, and excludes the Flink dependencies of 
`flink-kubernetes-operator-api`. No existing module's dependencies change.
   - The public API, i.e., is any changes to the `CustomResourceDescriptors`: 
**yes**. `FlinkBlueGreenDeployment` gains `spec.transitionMode` (`BASIC` / 
`ADVANCED`, default `BASIC`), immutable after the initial deployment.
   - Core observer or reconciler logic that is regularly executed: **yes**, for 
`FlinkBlueGreenDeployment` only. `FlinkService` gains `getMinSubtaskMetrics`, 
used only by the Blue/Green teardown. `FlinkDeployment` and `FlinkSessionJob` 
reconciliation is untouched, and `BASIC` behaviour is preserved.
   
   ## Documentation
   
   A new Advanced Transition Mode section under [Blue/Green 
Deployments](docs/content/docs/managing/bluegreen-deployments.md) covers how 
the hand-over works and what it guarantees, the teardown and aborts, the 
requirements, enabling it with the gate in the job's code, the extractor and 
gate placement. The Blue/Green requirements also name the savepoint format of 
transitions. Zero-Downtime Upgrades, Controllers, the custom resource overview, 
the glossary, the generated CRD reference and the Chinese pages are updated to 
match.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to