KKcorps opened a new pull request, #19499:
URL: https://github.com/apache/pinot/pull/19499

   ## TL;DR
   
   `UpsertCompactionTask`'s generator already compares valid doc counts across 
replicas and refuses
   to schedule a segment whose replicas disagree. Today that refusal is a log 
line and nothing else,
   so the controller notices that two replicas of an upsert table hold 
different data, quietly stops
   compacting the segment, and tells no one. This PR emits a meter for it, 
scoped to the one skip
   reason that actually means divergence.
   
   ## The problem
   
   `MinionTaskUtils.selectValidDocIdsMetadataForConsensus` is the generator's 
pre-scheduling gate. It
   returns `null` to skip a segment, and it does that for six different 
reasons. Five of them are
   "the answer is not trustworthy right now". One of them means the replicas 
have genuinely drifted
   apart: in `EQUAL` mode, two replicas report a different number of valid docs 
for the same segment
   with the same CRC, both servers healthy, all replicas accounted for.
   
   That last case is the interesting one and it is invisible. There is no 
metric on it, so a table can
   sit with diverged replicas for weeks, serving different answers depending on 
which replica the
   broker picks, while compaction silently stalls on the affected segments.
   
   ```mermaid
   flowchart LR
     subgraph Before["❌ Today"]
       A[replicas report<br/>different valid doc counts] --> B[generator skips 
segment]
       B --> C[LOGGER.warn]
       C --> D[nobody knows<br/>compaction stalls]
     end
     subgraph After["✅ With this PR"]
       E[replicas report<br/>different valid doc counts] --> F[generator skips 
segment]
       F --> G[LOGGER.warn names<br/>both servers and both counts]
       F --> H[UpsertCompactionSegmentsSkipped<br/>meter per table]
       H --> I[alertable]
     end
   ```
   
   ## The approach
   
   1. Add 
`ControllerMeter.UPSERT_COMPACTION_SEGMENT_SKIPPED_CONSENSUS_FAILURE`, a 
per-table meter
      exported as `UpsertCompactionSegmentsSkipped`.
   2. Give `selectValidDocIdsMetadataForConsensus` an optional 
`ControllerMetrics` and the table name,
      and bump that meter from the `EQUAL`-mode disagreement branch only.
   3. Thread `ControllerMetrics` and `tableNameWithType` from 
`UpsertCompactionTaskGenerator`'s
      `generateTasks` into `processValidDocIdsMetadata`, which is where the 
per-segment loop lives.
   4. Keep the existing five-argument `selectValidDocIdsMetadataForConsensus` 
as an overload that
      meters nothing, so the other caller is untouched.
   5. Extend the disagreement log line to name both servers and both counts.
   
   ## Which skip reasons are metered
   
   Only the last row. The other five are transient states that resolve on their 
own, and metering them
   would make the signal fire during every segment reload and every rolling 
restart.
   
   | Why the generator skips the segment | Metered | Why not |
   |---|---|---|
   | No replica reported metadata | no | Nothing to compare yet |
   | Replica's CRC will not parse | no | Bad response, not bad data |
   | Replica CRC does not match ZK | no | Usually a reload in flight |
   | A server is not in `GOOD` state | no | It may still be mutating the 
segment |
   | Fewer replicas responded than expected | no | Cannot confirm consensus 
either way |
   | `EQUAL` mode, replicas report different valid doc counts | **yes** | The 
replicas hold different data |
   
   This is also why the meter sits inside the helper rather than at the 
generator's
   `if (validDocIdsMetadata == null) { continue; }`. By the time the caller 
sees the `null`, the reason
   is gone.
   
   ## Flow
   
   ```mermaid
   sequenceDiagram
     participant G as UpsertCompactionTaskGenerator
     participant R as ServerSegmentMetadataReader
     participant S as Servers
     participant U as MinionTaskUtils
     participant M as ControllerMetrics
   
     G->>R: getSegmentToValidDocIdsMetadataFromServer
     R->>S: validDocIdsMetadata per segment batch
     S-->>R: per-replica valid doc counts, CRCs, server status
     R-->>G: segment -> List<ValidDocIdsMetadataInfo>
   
     loop per candidate segment
       G->>U: selectValidDocIdsMetadataForConsensus(..., metrics, 
tableNameWithType)
       alt CRC mismatch, unhealthy server, or short responder list
         U-->>G: null (skipped, not metered)
       else EQUAL mode and counts differ
         U->>M: addMeteredTableValue(UpsertCompactionSegmentsSkipped, 1)
         U-->>G: null (skipped, metered)
       else replicas agree
         U-->>G: chosen replica
       end
     end
   ```
   
   ## New metrics
   
   | Metric | Type | Meaning |
   |---|---|---|
   | `UPSERT_COMPACTION_SEGMENT_SKIPPED_CONSENSUS_FAILURE` 
(`UpsertCompactionSegmentsSkipped`) | Meter, per table | A segment was not 
scheduled for `UpsertCompactionTask` because its replicas reported different 
valid doc counts |
   
   No configuration changes. No JMX-to-Prometheus exporter changes either: 
`controller.yml` and
   `pinot.yml` both end in a catch-all pattern for table-scoped meters, so this 
exports as
   `pinot_controller_UpsertCompactionSegmentsSkipped_Count` with `table` and 
`tableType` labels.
   `YammerControllerPrometheusMetricsTest` enumerates every `ControllerMeter` 
and asserts each one
   exports correctly, and it passes with the new entry, so that name and label 
set is verified rather
   than assumed.
   
   ## Compatibility notes
   
   - `UpsertCompactMergeTask` is deliberately untouched. It keeps calling the 
five-argument
     `selectValidDocIdsMetadataForConsensus` overload, which meters nothing, 
and no file under
     `upsertcompactmerge/` is in this diff.
   - The existing, declared-but-unused
     `ControllerMeter.UPSERT_COMPACT_MERGE_SEGMENT_SKIPPED_CONSENSUS_FAILURE` 
is left exactly as it is.
     This PR does not wire it. Its name is specific to the compact-merge task, 
so reusing it for the
     compaction task would have exported a misleading metric name.
   - `UpsertCompactionTaskGenerator.processValidDocIdsMetadata` is 
`@VisibleForTesting public static`
     and gains two parameters: `tableNameWithType` first and a nullable 
`ControllerMetrics` last. Any
     out-of-tree caller of that method needs updating. Passing `null` for the 
metrics keeps the old
     behavior exactly.
   
   ## Performance considerations
   
   - Nothing new runs on the happy path. When the replicas agree, the added 
code is not reached.
   - On a disagreement the cost is one `addMeteredTableValue` call, which is a 
map lookup plus a meter
     mark, on a path that already just decided to skip work.
   - The extended log line reads two more fields off objects the code already 
holds, and only inside a
     branch that was already logging.
   
   ## Testing
   
   
`UpsertCompactionTaskGeneratorTest.testProcessValidDocIdsMetadataConsensusFailureMeter`
 drives
   `processValidDocIdsMetadata` through each case and asserts the meter's delta:
   
   | Case | Expected meter delta |
   |---|---|
   | Replicas agree | 0 |
   | CRC mismatch between a replica and ZK | 0 |
   | One server in `STARTING` | 0 |
   | Only one of two replicas responded | 0 |
   | Replicas report different valid doc counts, `EQUAL` mode | 1 |
   | Same disagreement under `MOST_VALID_DOCS` | 0, the mode picks a winner 
instead of skipping |
   | Same disagreement with a `null` `ControllerMetrics` | 0, and no exception |
   
   The existing `testProcessValidDocIdsMetadataConsensus` still covers the 
selection behavior itself and
   is unchanged apart from the two new arguments. The suite passes, as does
   `YammerControllerPrometheusMetricsTest` for the new metric's export.
   


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