suvodeep-pyne opened a new pull request, #19909:
URL: https://github.com/apache/pinot/pull/19909

   ## Summary
   
   On pauseless tables with dedup or partial upsert, 
`PinotLLCRealtimeSegmentManager.repairSegmentsInErrorState` returns early and 
repairs no ERROR segment at all. That includes segments whose other replica is 
ONLINE, where a reset only reloads data a peer or the deep store already holds. 
This PR keeps the dedup / partial-upsert guard on re-ingestion only, resets 
just the ERROR replicas, and makes the skipped repairs visible.
   
   ## Motivation
   
   `RealtimeSegmentValidationManager` runs this repair on every pauseless 
table. #16811 added an early return that skips all of it when 
`allowRepairOfCommittingSegments` is false, which is the default for dedup and 
partial-upsert tables:
   
   ```java
   if (isPauselessTable && !repairCommittingSegments) {
     // We do not run reingestion for dedup and partial upsert tables in 
pauseless as it can
     // lead to data inconsistencies
     ...
     return;
   }
   ```
   
   The comment names the actual risk, re-ingestion, but the return also skips 
the reset branches. A reset takes an ERROR replica through OFFLINE and back, so 
it downloads the committed segment from the deep store or from a peer. It never 
re-consumes, so it cannot add duplicate or inconsistent rows. Before #16811, 
those resets ran on pauseless dedup tables by default, because 
`controller.segment.error.autoReset` defaults to true. Non-pauseless dedup 
tables still run them.
   
   On a dedup or partial-upsert table, one unrepaired replica can stop the 
server consuming. The server's consumers wait until every segment the 
IdealState marks ONLINE for that server is ONLINE in its current state 
(`TableStateUtils.isAllSegmentsLoaded`). One ERROR replica keeps that check 
false, so the server stops consuming the table. Each later segment then fails 
to go ONLINE on that server too, and the repair never helps. Seen on a 
2-replica pauseless dedup table: 5 segments were ONLINE on one server and ERROR 
on the other. Every 15-minute validation run logged the same two lines and did 
nothing:
   
   ```
   Found 5 segments with at least one replica in ERROR state: [...], 0 segments 
with all replicas in ERROR state: [] in table: ..._REALTIME, repairing them
   Skipping repair for errored segments in table: ..._REALTIME because dedup is 
enabled
   ```
   
   Two related problems in the same method:
   
   - **The reset is not targeted.** `resetSegment(table, segment, null)` resets 
every replica in the IdealState, including the healthy ones. That can briefly 
leave a partition with zero ONLINE replicas. On a dedup or upsert table it also 
drops the healthy replica's keys from the metadata manager for a moment while 
other segments keep consuming. If the segment has no deep-store copy, it 
bounces the only good copy.
   - **The skip is not visible.** In the early-return path, 
`pauselessSegmentsInErrorCount` is never set and 
`pauselessSegmentsInUnrecoverableErrorCount` counts only segments with every 
replica in ERROR. Both read 0 in the case above.
   
   ## Changes
   
   - **Gate only re-ingestion.** The early return is removed. Re-ingestion of a 
COMMITTING segment with all replicas in ERROR stays gated on 
`allowRepairOfCommittingSegments`, which is the 
`repairErrorSegmentsForPartialUpsertOrDedup` task property or 
`disasterRecoveryMode=ALWAYS`. The reset branches now run for every table.
   - **Reset only the ERROR replicas.** `resetSegment` is now called once per 
replica that is in ERROR in the external view, not once per segment with no 
target. If one reset fails, the failure is logged and repair continues with the 
other segments. Before, one failure threw and stopped the whole pass.
   - **Make skipped repairs visible.**
     - Each skipped COMMITTING segment now logs a WARN that says how to enable 
the repair.
     - The `allowRepairOfCommittingSegments` log lines now say that only 
re-ingestion is disallowed.
     - `pauselessSegmentsInErrorCount` (and `segmentsInError` on non-pauseless 
tables) now counts segments with **any** ERROR replica, not only segments with 
every replica in ERROR.
     - `pauselessSegmentsInUnrecoverableErrorCount` counts only segments that 
this pass actually leaves unrepaired.
   
   ### Behavior change for reviewers
   
   - **Resets now run on pauseless dedup and partial-upsert tables without an 
override.** These are the same resets that pauseless tables without dedup, and 
non-pauseless dedup tables, already get. One of them resets an IN_PROGRESS 
segment with every replica in ERROR, which restarts consumption from the 
segment's start offset. That is what an operator does by hand today.
   - **`segmentsInError` now agrees more closely with `SegmentStatusChecker`.** 
For non-pauseless tables, `SegmentStatusChecker` also writes this gauge, as the 
number of ERROR replicas. `repairSegmentsInErrorState` used to overwrite it 
with the number of segments with every replica in ERROR, so the gauge often 
dropped to 0 between status-checker runs. The two counts are now equal whenever 
each errored segment has one ERROR replica.
   
   ## Testing
   
   - **New integration test `PauselessDedupErrorReplicaRepairTest`**
     - Setup: a 2-replica pauseless dedup table with **no** disaster-recovery 
override. Consuming-transition failures are injected on one server only, so 
every affected segment keeps one healthy replica. The test runs the 
segment-level validation and requires every ERROR replica to recover and the 
deduplicated count to be served.
     - On master it fails: the controller logs `Skipping repair for errored 
segments ... because dedup is enabled` on every pass.
     - With this change it passes.
   - **Existing `PauselessDedupRealtimeIngestionSegmentCommitFailureTest` still 
passes.** It runs with the override and covers commit failures and 
consuming-transition failures, so it shows the targeted resets and the 
re-ingestion path still recover fully.
   - **`PinotLLCRealtimeSegmentManagerTest` has two new unit tests:**
     - On a pauseless dedup table, the ERROR replicas of COMMITTING, DONE and 
IN_PROGRESS segments are each reset individually, and healthy replicas are 
never reset. The gauges read 4 segments with an ERROR replica and 1 left 
unrepaired. On master this test fails because no reset is issued.
     - With the override, the re-ingestion path is taken.
   - `PauselessRealtimeIngestionSegmentCommitFailureTest` (no dedup) and 
`RealtimeSegmentValidationManagerTest` still pass. Spotless, checkstyle and 
license checks pass on `pinot-controller` and `pinot-integration-tests`.
   


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