yangwwei commented on a change in pull request #250:
URL: 
https://github.com/apache/incubator-yunikorn-core/pull/250#discussion_r585277655



##########
File path: pkg/scheduler/objects/application.go
##########
@@ -225,6 +234,56 @@ func (sa *Application) clearStateTimer() {
                zap.String("state", sa.stateMachine.Current()))
 }
 
+func (sa *Application) isWaitingStateTimedOut() bool {
+       return sa.IsWaiting() && sa.stateTimer == nil
+}
+func (sa *Application) initPlaceholderTimer() {
+       if sa.placeholderTimer != nil || !sa.IsAccepted() || sa.execTimeout <= 
0 {
+               return
+       }
+       log.Logger().Debug("Application placeholder timer initiated",
+               zap.String("AppID", sa.ApplicationID),
+               zap.Duration("Timeout", sa.execTimeout))
+       sa.placeholderTimer = time.AfterFunc(sa.execTimeout, 
sa.timeoutPlaceholderProcessing)
+}
+
+func (sa *Application) clearPlaceholderTimer() {
+       if sa == nil || sa.placeholderTimer == nil {
+               return
+       }
+       sa.placeholderTimer.Stop()
+       sa.placeholderTimer = nil
+}
+
+func (sa *Application) timeoutPlaceholderProcessing() {

Review comment:
       Why do we need to handle different cases? 
   In my opinion, at the app level, we simply do start the placeholderTimer 
(when we get the first allocation) and stop it when the condition reaches: 1) 
all placeholders are replaced; 2) app transited to completed; 
   when the timer times out, the handling is very simple, we just clean up all 
the existing placeholder allocations and pending placeholder asks. 
   

##########
File path: pkg/scheduler/objects/application.go
##########
@@ -1015,8 +1072,11 @@ func (sa *Application) GetAllAllocations() []*Allocation 
{
 
 // get a copy of all placeholder allocations of the application
 // No locking must be called while holding the lock
-func (sa *Application) getPlaceholderAllocations() []*Allocation {
+func (sa *Application) GetPlaceholderAllocations() []*Allocation {

Review comment:
       why do we need to expose this function?
   note this function doesn't hold any locks, which implies it is supposed to 
be only used internally

##########
File path: pkg/scheduler/objects/application.go
##########
@@ -1025,6 +1085,17 @@ func (sa *Application) getPlaceholderAllocations() 
[]*Allocation {
        return allocations
 }
 
+func (sa *Application) GetAllRequests() []*AllocationAsk {

Review comment:
       No need to expose this function

##########
File path: pkg/scheduler/objects/application.go
##########
@@ -1103,11 +1177,19 @@ func (sa *Application) removeAllocationInternal(uuid 
string) *Allocation {
                sa.allocatedResource = resources.Sub(sa.allocatedResource, 
alloc.AllocatedResource)
        }
        // When the resource trackers are zero we should not expect anything to 
come in later.
-       if resources.IsZero(sa.pending) && 
resources.IsZero(sa.allocatedResource) && 
resources.IsZero(sa.allocatedPlaceholder) {
-               if err := sa.HandleApplicationEvent(WaitApplication); err != 
nil {
-                       log.Logger().Warn("Application state not changed to 
Waiting while removing some allocation(s)",
-                               zap.String("currentState", sa.CurrentState()),
-                               zap.Error(err))
+       if resources.IsZero(sa.pending) && 
resources.IsZero(sa.allocatedResource) {
+               if sa.isWaitingStateTimedOut() && 
resources.IsZero(sa.allocatedPlaceholder) {
+                       if err := 
sa.HandleApplicationEvent(CompleteApplication); err != nil {
+                               log.Logger().Warn("Application state not 
changed to Completed while removing some allocation(s)",
+                                       zap.String("currentState", 
sa.CurrentState()),
+                                       zap.Error(err))
+                       }
+               } else {
+                       if err := sa.HandleApplicationEvent(WaitApplication); 
err != nil {
+                               log.Logger().Warn("Application state not 
changed to Waiting while removing some allocation(s)",
+                                       zap.String("currentState", 
sa.CurrentState()),
+                                       zap.Error(err))
+                       }

Review comment:
       This part is quite confusing to me.
   Why do we need to handle `CompleteApplication` here in a special case?
   
   According to the doc: 
http://yunikorn.apache.org/docs/next/design/scheduler_object_states. We claim 
an app is in `Completed` state only when: _An application is considered 
completed when it has been in the waiting state for a defined time period._
   
   If an app has gang members, and there are placeholders (either ask or 
allocation), we do not transit the app to `Waiting` state. Instead, it is in 
`Running` state with a `placeholderTimer`. The placeholder timer determines 
"_the max duration an app can run from the start of applying placeholders to 
all placeholders is replaced_". When it times out, we trigger a clean for all 
existing placeholders (no matter they are allocated or pending). Then once all 
cleanup are done, the app will enter `Waiting` state (use the old logic).
   
   Would this be easier?

##########
File path: pkg/scheduler/objects/application.go
##########
@@ -1037,6 +1108,9 @@ func (sa *Application) AddAllocation(info *Allocation) {
 func (sa *Application) addAllocationInternal(info *Allocation) {
        // placeholder allocations do not progress the state of the app and are 
tracked in a separate total
        if info.placeholder {
+               if resources.IsZero(sa.allocatedPlaceholder) {
+                       sa.initPlaceholderTimer()
+               }

Review comment:
       please add some comment, why we `initPlaceholderTimer()` here?
   I think we have discussed, but could not remember the answer to the 
following question:
   what if we cannot get even one placeholder allocated for an app? does that 
mean the app will be stuck forever? It could happen if the job's pods were 
created with some incorrect resource sizes (e.g bigger than node capability), 
wrong node selector, tolerations, etc.

##########
File path: pkg/scheduler/objects/application.go
##########
@@ -225,6 +234,56 @@ func (sa *Application) clearStateTimer() {
                zap.String("state", sa.stateMachine.Current()))
 }
 
+func (sa *Application) isWaitingStateTimedOut() bool {
+       return sa.IsWaiting() && sa.stateTimer == nil
+}
+func (sa *Application) initPlaceholderTimer() {
+       if sa.placeholderTimer != nil || !sa.IsAccepted() || sa.execTimeout <= 
0 {
+               return
+       }
+       log.Logger().Debug("Application placeholder timer initiated",
+               zap.String("AppID", sa.ApplicationID),
+               zap.Duration("Timeout", sa.execTimeout))
+       sa.placeholderTimer = time.AfterFunc(sa.execTimeout, 
sa.timeoutPlaceholderProcessing)
+}
+
+func (sa *Application) clearPlaceholderTimer() {
+       if sa == nil || sa.placeholderTimer == nil {
+               return
+       }
+       sa.placeholderTimer.Stop()
+       sa.placeholderTimer = nil
+}
+
+func (sa *Application) timeoutPlaceholderProcessing() {
+       sa.Lock()
+       defer sa.Unlock()
+       var allocationsToRelease []*Allocation
+       // Case 1: if all app's placeholders are allocated, only part of them 
gets replaced, just delete the remaining placeholders
+       switch {
+       case sa.allPlaceholdersAllocated() && !sa.allPlaceholdersReplaced():

Review comment:
       the first check seems wrong. the `allocatedPlaceholder` will be 
decreased if there is any placeholder replacement happened. so even all 
placeholder were allocated, after there is any replacement, this is always 
false.




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

For queries about this service, please contact Infrastructure at:
[email protected]


Reply via email to