0AyanamiRei opened a new pull request, #67678:
URL: https://github.com/apache/doris/pull/67678

   ### What problem does this PR solve?
   
   Issue Number: N/A
   
   Related PR: N/A
   
   Problem Summary:
   
   Kinesis Routine Load can skip records when a shard starts at `LATEST` and 
its first task does not commit a concrete sequence number. The BE has resolved 
`LATEST` into a shard iterator, but FE progress still contains the symbolic 
position. Empty polling, an aborted transaction, a timeout, or task replacement 
can discard the iterator. The replacement task then interprets `LATEST` again 
against a later stream tip.
   
   For example:
   
   ```text
   1. A shard contains R1, R2, R3; task A obtains a LATEST iterator after R3.
   2. R4 and R5 arrive.
   3. Task A aborts or is replaced before committing a sequence number.
   4. FE still supplies LATEST to task B.
   5. Task B obtains an iterator after R5, skipping R4 and R5.
   ```
   
   Kafka resolves symbolic offsets through its watermark metadata API before 
consumption. Kinesis `GetShardIterator(LATEST)` returns an opaque iterator, not 
a durable numeric end offset, and an empty `GetRecords` response does not 
reveal the preceding record's sequence number. The five-minute expiration 
applies to the iterator; a record sequence number can be used to obtain another 
iterator while the source position remains available within retention.
   
   This PR resolves initial Kinesis positions before creating data tasks, 
persists the resolved positions in FE, and keeps the existing task and 
transaction progress protocol.
   
   #### 1. Resolve only the shards that need initialization
   
   FE selects the intersection of the job's currently tracked **OPEN shards** 
and progress entries still set to `LATEST` (case insensitive) or `-1`.
   
   - Concrete sequence numbers and `TRIM_HORIZON` positions are reused without 
scanning.
   - Shards already known to be CLOSED do not participate in initialization 
scanning; existing closed-shard consumption/cleanup remains in place.
   - Stale progress entries left after reducing a custom shard selection do not 
trigger unnecessary scans.
   - Existing shard discovery supplies the OPEN/CLOSED tracking; this change 
does not add discovery calls just to determine that state.
   
   `prepare()` submits one asynchronous metadata RPC containing the batch. The 
job remains `NEED_SCHEDULE` while the result is pending. 
`divideRoutineLoadJob()` checks the same unresolved set and does not create 
tasks until resolution and persistence have completed. No data-task transaction 
is opened while waiting for this scan.
   
   #### 2. Find restartable positions by scanning retained records
   
   For each selected shard, the BE requests a `TRIM_HORIZON` iterator and 
follows `GetRecords` pages, keeping the last record's sequence number. It 
continues across empty intermediate pages and finishes when the service reports 
`MillisBehindLatest == 0` or the iterator reaches a closed shard's end.
   
   | Scan result | Persisted FE position | Subsequent task behavior |
   |---|---|---|
   | The shard tip is R3 | `sequence(R3)` | Read with 
`AFTER_SEQUENCE_NUMBER(R3)` |
   | The scan reaches the tip without seeing any records | `TRIM_HORIZON` | 
Read the earliest retained records, including later arrivals |
   | A shard fails, the batch times out, or initialization is cancelled | No 
positions from that batch are published | Keep initialization unresolved |
   
   The scan downloads historical payloads but never sends them to the Doris 
load pipe. Its total work is `O(sum(L_i))`, or `O(N * L)` for N shards with 
average retained length L. Each worker retains a response page rather than 
buffering the entire history.
   
   #### 3. Bound concurrency across all initialization requests on a BE
   
   A dedicated BE scan pool is shared by all batches, with **8 workers by 
default**. Each batch submits at most `min(shard_count, 
configured_worker_count)` workers. Workers claim shards within their batch, and 
each worker owns its scan consumer/client.
   
   Scanning is dispatched directly to this pool, so ordinary Routine Load 
workers do not block waiting for long metadata scans. The queue is bounded to 
1024 workers; submission failure fails the batch and causes already submitted 
workers to stop. This is a concurrency bound, not a guarantee of fair 
scheduling between jobs.
   
   The result map is protected by a mutex. Completion accounting includes both 
executed workers and failed submissions. The response and RPC `done` callback 
are completed only after all workers have exited, so no worker can continue 
using the RPC controller after it is released.
   
   #### 4. Persist the complete batch before scheduling tasks
   
   ```text
   FE receives the complete successful response
     -> verify RPC status and requested shard coverage
     -> synchronously journal KinesisLatestPositionOperation(jobId, positions)
     -> apply positions to existing KinesisProgress
     -> create tasks from that progress
     -> begin transactions and use the existing task protocol
   ```
   
   The new operation is wired into journal serialization and replay. The 
existing job image continues to persist the progress map. A crash after 
journaling but before applying the in-memory update recovers the positions 
through replay.
   
   No successful subset is persisted or reused after a failed initialization 
attempt. Once the batch is persisted, an abort or replacement uses the saved 
position instead of choosing a newer tip. Successful data transactions continue 
advancing progress through the original attachment path; no changes are made to 
the data-task Thrift schema or transaction commit behavior.
   
   #### 5. Make cancellation visible to the BE
   
   The current brpc implementation does not reliably expose cancellation of an 
individual gRPC stream through `Controller::IsCanceled()`, which observes 
socket failure. Each initialization batch therefore uses its own gRPC channel, 
created through the existing channel provider and DNS handling. A Future 
listener calls non-blocking `shutdownNow()` on completion, failure, or 
cancellation.
   
   PAUSE, terminal job states, explicit source ALTER, and an enabled total 
timeout cancel the pending initialization. Closing its dedicated connection 
lets the BE observe cancellation without closing connections used by unrelated 
requests. This does not add a cancel RPC or a scan-registration handshake.
   
   Workers check shared batch status before claiming shards, around AWS calls, 
and during backoff. AWS requests also receive a continuation callback. The BE 
checks server shutdown as well, preventing an unlimited scan from blocking brpc 
shutdown before the executor's stop phase.
   
   #### Configuration and timeout semantics
   
   | Setting | Location | Default | Meaning |
   |---|---|---|---|
   | `kinesis_latest_sequence_timeout_second` | FE, mutable | `-1` | Total 
initialization budget; -1 disables it, a positive value is seconds |
   | `kinesis_latest_sequence_scan_threads` | BE, startup | `8` | Maximum 
concurrent scan workers shared by that BE |
   | `kinesis_latest_sequence_request_timeout_ms` | BE, startup | `30000` | 
Finite total timeout for each scan-client Kinesis HTTP request |
   
   A positive overall timeout covers the batch, including BE queueing, and is 
never reset per shard or successful page. FE also sets a gRPC deadline and 
rejects results collected after its deadline. BE workers share one local 
monotonic deadline established before queueing.
   
   Scan clients use the SDK's actual HTTP timeout (`httpRequestTimeoutMs`), 
independently of the ordinary consumers' settings. SDK-internal retries are 
disabled for these clients; iterator acquisition and record reads use up to 
three cancellable retries with 1, 2, and 4 second backoffs.
   
   Cancellation is cooperative: the SDK continuation handler does not 
immediately interrupt every silent network wait. An in-flight Kinesis HTTP call 
may take until its finite request timeout to finish. Credential-provider 
initialization/refresh is not fully covered by that Kinesis HTTP timeout.
   
   #### Semantics and compatibility boundaries
   
   - Each shard's initial boundary is the tip observed when its scan catches 
up. Records encountered during initialization belong to the skipped initial 
prefix. This is not a CREATE-time cutoff or a cross-shard snapshot.
   - All-or-nothing persistence avoids retaining some shards' positions across 
later failed attempts; it does not make the observations within one scan 
simultaneous. With an unlimited total timeout, their time difference has no 
fixed upper bound.
   - Source retention still limits recovery; this change does not recover data 
already skipped by an older version or expired from Kinesis.
   - An older BE that ignores the new request field returns no position map, 
which the FE rejects instead of scheduling unresolved positions.
   - The new journal operation must be understood by replaying FEs. 
Mixed-version upgrade/downgrade procedures must account for it.
   - The ordinary Kinesis consume loop, EFO, and the data-task transaction 
protocol are outside this patch.
   
   AWS API references: 
[GetShardIterator](https://docs.aws.amazon.com/kinesis/latest/APIReference/API_GetShardIterator.html),
 
[GetRecords](https://docs.aws.amazon.com/kinesis/latest/APIReference/API_GetRecords.html).
   
   ### Release note
   
   Fix Kinesis Routine Load initialization from `LATEST` losing its starting 
position after empty polling, transaction aborts, timeouts, or task 
replacement. Initial positions are resolved and journaled before data tasks are 
created, using bounded BE scan concurrency.
   
   ### Check List (For Author)
   
   - Test
       - [ ] Regression test
       - [x] Unit Test
       - [ ] Manual test (real AWS/network cancellation was not exercised)
       - [ ] No need to test or manual test
   - Behavior changed:
       - [ ] No.
       - [x] Yes. Initial OPEN `LATEST` shards are scanned before scheduling, 
complete results are journaled, and PAUSE cancels pending initialization.
   - Does this need documentation?
       - [ ] No.
       - [x] Yes. New settings and initialization semantics are described 
above; a website documentation PR is not included.
   
   #### Validation performed
   
   | Check | Result |
   |---|---|
   | FE build/package with UI disabled | Passed |
   | `KinesisLatestPositionTest` | 8 passed |
   | `KinesisDataSourcePropertiesTest` | 4 passed |
   | BE ASAN_UT build/link and `KinesisLatestSequenceTest.*` | 7 passed; 
rebuilt and rerun after the scan-helper cleanup |
   | Existing `RoutineLoadTaskExecutorTest.*` | 1 passed |
   | clang-format 16, BE header hygiene, Java Checkstyle, diff whitespace 
checks | Passed |
   
   Commands used (the local Arrow 24/Paimon installation uses the unversioned 
prefix):
   
   ```bash
   DISABLE_BUILD_UI=ON BUILD_TYPE=ASAN ./build.sh --fe -j48
   ./run-fe-ut.sh --run 'org.apache.doris.load.routineload.kinesis.*Test'
   
   ARROW_HOME="$PWD/thirdparty/installed" 
PAIMON_HOME="$PWD/thirdparty/installed" \
     ./run-be-ut.sh --run --filter='KinesisLatestSequenceTest.*' -j48
   ARROW_HOME="$PWD/thirdparty/installed" 
PAIMON_HOME="$PWD/thirdparty/installed" \
     ./run-be-ut.sh --run --filter='RoutineLoadTaskExecutorTest.*' -j48
   ```
   
   Validation limitations:
   
   - The separate production BE build was blocked by the build script's 
required versioned/dual-version thirdparty layout. The successful BE build 
above is the **ASAN_UT** build, not a production BE package.
   - Full clang-tidy did not pass because of an existing unmatched `NOLINTEND` 
in `be/src/core/types.h` and existing whole-file warnings. The newly added 
functions' complexity and request-copy diagnostics were fixed. A focused rerun 
of the two adjusted C++ files reported no diagnostics on their final changed 
lines; this is not a claim that full clang-tidy passed.
   - UTs use mocked Kinesis responses. Real AWS behavior, cross-job concurrency 
limits under load, connection-close cancellation latency, and shutdown under 
stalled network I/O have not been integration-tested.
   
   ### Check List (For Reviewer who merge this PR)
   
   - [ ] Confirm the release note
   - [ ] Confirm test cases
   - [ ] Confirm document
   - [ ] Add branch pick label
   


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


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to