KKcorps commented on PR #19998:
URL: https://github.com/apache/pinot/pull/19998#issuecomment-6437148363

   **This closes the order from the last comment. The model found one much 
narrower path to the same duplicate, and today's code reaches that duplicate 
through another order too.**
   
   None of the model's 45 known bad orders changed. An hour of search over 8 
table shapes, plus a search started next to a destroyed segment, found only 
this order.
   
   **The order that is left.** A Helix thread publishes N2's marker and stalls 
before reading the max. A reload then destroys the old N2, but the cap loop's 
iterator still returns its entry, so the lock fails and the round persists no 
watermark. The stall has to span the whole replace, so this is far rarer than 
the last order.
   
   ```mermaid
   sequenceDiagram
       participant T as Helix add N1
       participant R as Helix reload N2
       participant C as Consumer N4
       participant M as Upsert manager
       participant H as Helix N0
       T->>M: A@4 beats A@2 in N2, putIfAbsent(N2, NOT_SET)
       R->>M: hold N2's segment lock
       C->>M: snapshot skips N2, cap iterator reaches the old N2 entry
       R->>M: replaceSegment(N2), untrackSegment(old N2)
       R->>M: old N2 destroyed
       C->>M: entry is NOT_SET, tryAcquireReadLock(old N2) fails
       M->>M: persist TTL_WATERMARK_NOT_SET
       T->>M: tryAcquireReadLock(old N2) fails, return
       C->>M: removeExpiredPrimaryKeys drops A, its row A@4 in N1 stays valid
       H->>M: N0 OFFLINE then ONLINE, addSegment(N0) in full
       Note over M: A@0 finds no key, so A@0 and A@4 are both valid
   ```
   
   <details><summary>Model history</summary>
   
   ```
   ('append', 'A', (0,), 0, {'v': 1}, False)
   ('append', 'C', (0,), 1, {'v': 1}, False)
   ('assign_consumer', "N0'")
   ('acquire', "N0'")
   ('consume', "N0'", 0)
   ('consume', "N0'", 1)
   ('commit_local', "N0'", 1)
   ('step', 'replace', "N0'", 'start_replace')
   ('step', 'replace', "N0'", 'read_marked_max')
   ('step', 'replace', "N0'", 'add_committed_row:A@0')
   ('step', 'replace', "N0'", 'add_committed_row:C@1')
   ('step', 'replace', "N0'", 'begin_leftover')
   ('step', 'replace', "N0'", 'untrack_and_offload')
   ('append', 'A', (4,), 2, {'v': 1}, False)
   ('assign_consumer', "N1'")
   ('acquire', "N1'")
   ('consume', "N1'", 2)
   ('commit_local', "N1'", 2)
   ('step', 'replace', "N1'", 'start_replace')
   ('step', 'replace', "N1'", 'read_marked_max')
   ('step', 'replace', "N1'", 'add_committed_row:A@2')
   ('step', 'replace', "N1'", 'begin_leftover')
   ('step', 'replace', "N1'", 'untrack_and_offload')
   ('offline_immutable', 'N1')
   ('step', 'offline', 'N1', 'begin_remove')
   ('step', 'offline', 'N1', 'remove:A')
   ('step', 'offline', 'N1', 'untrack')
   ('append', 'A', (2,), 3, {'v': 1}, False)
   ('append', 'B', (5,), 4, {'v': 1}, False)
   ('assign_consumer', "N2'")
   ('acquire', "N2'")
   ('consume', "N2'", 3)
   ('consume', "N2'", 4)
   ('commit_local', "N2'", 4)
   ('step', 'replace', "N2'", 'start_replace')
   ('step', 'replace', "N2'", 'read_marked_max')
   ('step', 'replace', "N2'", 'add_committed_row:A@3')
   ('step', 'replace', "N2'", 'add_committed_row:B@4')
   ('step', 'replace', "N2'", 'begin_leftover')
   ('step', 'replace', "N2'", 'untrack_and_offload')
   ('append', 'D', (9,), 5, {'v': 1}, False)
   ('assign_consumer', "N3'")
   ('acquire', "N3'")
   ('consume', "N3'", 5)
   ('commit_local', "N3'", 5)
   ('step', 'replace', "N3'", 'start_replace')
   ('step', 'replace', "N3'", 'read_marked_max')
   ('step', 'replace', "N3'", 'add_committed_row:D@5')
   ('step', 'replace', "N3'", 'begin_leftover')
   ('step', 'replace', "N3'", 'untrack_and_offload')
   ('add_immutable', 'N1')
   ('step', 'add_immutable', 'N1', 'start_add')
   ('step', 'add_immutable', 'N1', 'read_marked_max')
   ('step', 'add_immutable', 'N1', 'add_row:A@2')
   ('reload', 'N2')
   ('assign_consumer', "N4'")
   ('acquire', "N4'")
   ('step', 'reload', 'N2', 'start_replace')
   ('step', 'reload', 'N2', 'read_marked_max')
   ('step', 'reload', 'N2', 'add_row:A@3')
   ('step', 'reload', 'N2', 'add_row:B@4')
   ('step', 'reload', 'N2', 'begin_leftover')
   ('step', 'reload', 'N2', 'untrack')
   ('destroy_segment', 6)
   ('step_watermark_cap',)
   ('step_watermark_cap',)
   ('step', 'add_immutable', 'N1', 'read_marked_max')
   ('step', 'add_immutable', 'N1', 'register')
   ('sweep_expired_keys', ['C', 'A', 'B'])
   ('offline_immutable', 'N0')
   ('step', 'offline', 'N0', 'begin_remove')
   ('step', 'offline', 'N0', 'skip_out_of_ttl')
   ('step', 'offline', 'N0', 'untrack')
   ('add_immutable', 'N0')
   ('step', 'add_immutable', 'N0', 'start_add')
   ('step', 'add_immutable', 'N0', 'read_marked_max')
   ('step', 'add_immutable', 'N0', 'add_row:A@0')
   ('step', 'add_immutable', 'N0', 'add_row:C@1')
   ('step', 'add_immutable', 'N0', 'register')
   ```
   
   </details>
   
   Keeping the comparison column's max on heap, as the old metadata did, would 
close this. It would also close a Helix thread marking a segment already 
removed out of TTL, which the model cannot build.
   
   The model covers one replica and one partition, and not the mapped metadata 
sidecar.
   
   <!-- upsert-model-check head=7cc009c556ca63a889901d22621fac41ad589d00 -->
   


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