Repository navigation
Time out RestrictionTrackers.trySplit after 5 minutes on lock contention - #40251
Conversation
|
Would this change break previously working slow split, in particular the initial split? After #36750, initial trySplit costs >10 min are tolerated. In fact, it was a response to scenarios of initial split cost >10 min worked for Dataflow v1 but not v2. I'm afraid this change will cause those pipeline failing again (initial split return null) |
It will only fail if we hit a 5 minute timeout trying to acquire the lock. Once the lock is acquired, we can take just as long to perform the split. |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #40251 +/- ##
============================================
+ Coverage 57.29% 58.56% +1.27%
- Complexity 3696 13700 +10004
============================================
Files 1201 2579 +1378
Lines 194397 269892 +75495
Branches 3889 11138 +7249
============================================
+ Hits 111373 158055 +46682
- Misses 79416 105845 +26429
- Partials 3608 5992 +2384
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
Checks are failing. Will not request review until checks are succeeding. If you'd like to override that behavior, comment |
| } catch (InterruptedException e) { | ||
| Thread.currentThread().interrupt(); | ||
| } | ||
| return null; |
There was a problem hiding this comment.
trySplit has this comment:
- @return a {@link SplitResult} if a split was possible, otherwise returns {@code null}. If the
-
{@code fractionOfRemainder == 0}, a {@code null} result MUST imply that the restriction -
tracker is done and there is no more work left to do.
So it seems like data loss to return null if fractionOfRemainder == 0.
There was a problem hiding this comment.
That's a good catch. I think in that case, we have 2 options:
- Raise a runtime exception
- Time out (keep the current behavior).
I lean towards keeping the current behavior for 2 reasons:
- It is less invasive
- It allows jobs to succeed if acquiring the split lock takes e.g. 8 minutes (so no potential regression)
I updated to do this, let me know what you think. It does mean we could fail a work item on a runner-initiated checkpoint, but at least the odds of doing that 4 times in a row should be low. I guess its also more motivation to move forward with your fix.
Btw, I think that the null protection is mostly for this code path -
- which would actually be ok since the lock would no longer be held after processElement returned. But I do think we should honor this to protect against data loss here nonetheless.|
Assigning reviewers: R: @ahmedabu98 for label java. This pull request likely touches a core component ("core" label). Please review with scrutiny. Note: If you would like to opt out of this review, comment Available commands:
The PR bot will only process comments in the main thread (not review comments). |
…ion (#40251) * Time out RestrictionTrackers.trySplit after 5 minutes on lock contention * Block on lock.lock() when fractionOfRemainder == 0 in trySplit
Why This Is Needed
In #40205, we updated
RestrictionTrackers.RestrictionTrackerObserverWithProgress.getProgress()to uselock.tryLock(60, TimeUnit.SECONDS)and return the cachedlastProgresson lock timeout so that a long-runningtryClaim()(such as a filtered scan inBoundedSourceAsSDFWrapperFn) does not block progress reporting indefinitely.However, in runner harnesses (such as Dataflow's worker progress/split update loop),
getProgress()andtrySplit()execute sequentially within the same control-plane progress update cycle and share the same 10-minute lease renewal deadline:getProgress()is called to gather bundle progress. With Return last evaluated progress on lock timeout in RestrictionTrackers.getProgress #40205, iftryClaim()is holdingRestrictionTrackers.lockfor an extended period (e.g. >10 minutes),getProgress()times out after 60 seconds and returnslastProgress.lastProgressis sent to the runner backend. Because the work item's reported progress is not advancing while other shards complete, dynamic work rebalancing (liquid sharding) flags the shard as a straggler and replies with a split request (ProcessBundleSplitRequest).RestrictionTrackerObserver.trySplit()as part of handling the update response—which callslock.lock()on the exact sameReentrantLockstill held by the long-runningtryClaim().trySplit()blocks indefinitely onlock.lock(), stalling the worker's progress/lease-renewal loop until the 10-minute deadline expires and fails the work item lease.Solution & Why It Is Safe
This change updates
RestrictionTrackerObserver.trySplit()to uselock.tryLock(300, TimeUnit.SECONDS)(5 minutes) instead of an unboundedlock.lock(), returningnullif the lock cannot be acquired within the timeout:trySplitcontract: In Apache Beam's SplittableDoFnspecification,RestrictionTracker.trySplit()is always advisory and is explicitly permitted to returnnullwhenever a restriction cannot be split at the current moment.lock.tryLocktimes out,delegate.trySplit()is never invoked, so the active restriction,ClaimObserverstate, and cached progress remain untouched.nullcausesFnApiDoFnRunnerto return an emptyProcessBundleSplitResponse, allowing the runner's progress/update loop to complete normally and keep renewing the work item lease. On subsequent progress update cycles, the runner can continue requesting splits, which will succeed as soon as the slowtryClaim()finishes and releaseslock.getProgress()(60s) +trySplit()(300s) always complete well within a 10-minute runner lease renewal window.