Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

KEP-2170: Add the TrainJob state transition design #2298

Merged

Conversation

tenzen-y
Copy link
Member

What this PR does / why we need it:
I added the TrainJob state machine design.

Which issue(s) this PR fixes (optional, in Fixes #<issue number>, #<issue number>, ... format, will close the issue(s) when PR gets merged):
Part-of #2207
Relates to: #2170

Checklist:

  • Docs included if any changes are user facing

@tenzen-y
Copy link
Member Author

/hold
/assign @kubeflow/wg-training-leads

@coveralls
Copy link

coveralls commented Oct 20, 2024

Pull Request Test Coverage Report for Build 11645733536

Details

  • 0 of 0 changed or added relevant lines in 0 files are covered.
  • No unchanged relevant lines lost coverage.
  • Overall coverage remained the same at 100.0%

Totals Coverage Status
Change from base Build 11636357716: 0.0%
Covered Lines: 77
Relevant Lines: 77

💛 - Coveralls

@tenzen-y tenzen-y force-pushed the add-trainjob-state-transition branch from 851738c to 069345f Compare October 20, 2024 22:00
@google-oss-prow google-oss-prow bot added size/M and removed size/L labels Oct 20, 2024
@tenzen-y tenzen-y force-pushed the add-trainjob-state-transition branch 2 times, most recently from 39bfab2 to 8ba2206 Compare October 20, 2024 22:28
Copy link
Member

@andreyvelich andreyvelich left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for this @tenzen-y!
Please take a look @kubeflow/wg-training-leads @kannon92 @akshaychitneni @varshaprasad96 @ahg-g @vsoch @danielvegamyhre

// TrainJobSuspended means the TrainJob is suspended.
TrainJobSuspended string = "Suspended"

// TrainJobCompleted means that the actual jobs have completed its execution.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should it be TrainJob, and similar for other statuses ?

Suggested change
// TrainJobCompleted means that the actual jobs have completed its execution.
// TrainJobCompleted means that the TrainJob has completed its execution.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@@ -279,6 +279,43 @@ type TrainJob struct {
Status TrainJobStatus `json:"status,omitempty"`
}

const (
Copy link
Member

@andreyvelich andreyvelich Oct 21, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we define those consts as TrainJobConditionType similar to Batch/Job: https://github.com/kubernetes/api/blob/master/batch/v1/types.go#L617 ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think so since our conditions typed are metav1.Condition and the Type is typed string.
The batch/v1 Job uses the typed JobConditionType since Job conditions typed are dedicated JobCondition: https://github.com/kubernetes/api/blob/2be23f6c5a7184af9846ff458a11751765ea2bde/batch/v1/types.go#L662

When we use the dedicated typed TrainJobConditionType, we need to cast the TrainJobConditionType to string everywhere. That is not ideal.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, you are right.


// TrainJobResumedReason is the "Suspended" condition reason.
// When the TrainJob suspension is changed from True to False, this is added.
TrainJobResumedReason string = "Resumed"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tenzen-y @kannon92 Do we have that status reason in JobSet or Job ?

Copy link
Member Author

@tenzen-y tenzen-y Oct 21, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, we have. In the batch/v1 Job level, JobResumed is used as a reason for the Suspended condition type.
In the JobSet level, ResumeJobs is used as a reason for the Suspended condition type.

Comment on lines +305 to +316
// TrainJobJobsCreationSucceededReason is the "Created" condition reason.
// When the creating objects succeeded after building succeeded, this is added.
TrainJobJobsCreationSucceededReason string = "JobsCreationSucceeded"

// TrainJobJobsBuildFailedReason is the "Created" condition reason.
// When the building objects based on the TrainJob and the specified runtime failed,
// this is added.
TrainJobJobsBuildFailedReason string = "JobsBuildFailed"

// TrainJobJobsCreationFailedReason is "Created" condition reason.
// When the creating objects failed even though building succeeded, this is added.
TrainJobJobsCreationFailedReason string = "JobsCreationFailed"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we introduce those conditions in the 2nd iteration ?
I think, we should discuss if users really want to decouple conditions when TrainJob's object creation failed and when TrainJob fails.

Copy link
Member Author

@tenzen-y tenzen-y Oct 21, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are reasons, so we use these in the following. Additionally, if we do not distinguish JobCreationFailed and JobBuildFailed, it is hard to understand which part failed. So, these reasons must be included in the first phase.

status:
  conditions:
  - type: Created
    status: false
    reason: JobsCreationFailed
status:
  conditions:
  - type: Created
    status: false
    reason: JobsBuildFailed
status:
  conditions:
  - type: Created
    status: true
    reason: JobsCreationSucceeded

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we have validation webhook, what is the use-case you see when we can hit the JobsBuildFailed reason ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can not validate in advance if NewObject succeed since we can not know in advance what happens during the NewObject.

Only during reconciling, we can know the actual error.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, I guess one of the examples could be if NewObject calls Kubernetes API server and this call fails.
In that case, we want to transition TrainJob to JobsBuildFailed status.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In that case, we want to transition TrainJob to JobsBuildFailed status.

Yes, that's right. In the current JobSet plugin, the building error could happen in the following:

Additionally, every plugin could return errors during Kubeflow Job Pipeline Framerowk.

Comment on lines 1000 to 1003
- type: JobSetSuspended
status: false
- type: JobSetCompleted
status: true
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need to propagate JobSet conditions to the TrainJob ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I propagated these JobSet conditions so that runtimes can be propagated to the arbitrary conditions.
But, in the JobSet case, these seem to be slightly redundant. So, I'm wondering if we only extend the runtime interface so that the plugin can propagate arbitrary conditions, but JobSet plugin does not propagate own conditions, then TrainJob does not have the JobSet conditions.

But, I guess that the JobSet StartupPolocy conditions could help to understand what happens in the actual Jobs.
So, we can propagate only StartupPolicyInProgress and StartupPolicyCompleted.

https://github.com/kubernetes-sigs/jobset/blob/440f53e13ed1db15d4f1d5a04c2450e74df4d1e8/api/jobset/v1alpha2/jobset_types.go#L75-L78

@andreyvelich WDYT?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, I'm wondering if we only extend the runtime interface so that the plugin can propagate arbitrary conditions, but JobSet plugin does not propagate own conditions, then TrainJob does not have the JobSet conditions.

I would suggest that we add those once we have initial implementation of TrainJob status.
I think, this is valid option, but we should discuss what conditions be be propagated by runtime plugins.

Any thoughts @tenzen-y ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would suggest that we add those once we have initial implementation of TrainJob status.
I think, this is valid option, but we should discuss what conditions be be propagated by runtime plugins.

I think that we need to implement the conditions propagation mechanism, but it's not clear in the JobSet situation. Especially, which conditions should be propagated to the TrainJob. So, I would propose that we extend the runtime interface so that plugins can propagate the conditions to TrainJob. But, in the first status implementation, we do not propagate any JobSet conditions to the TrainJob.

What do you think? @andreyvelich

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, I would propose that we extend the runtime interface so that plugins can propagate the conditions to TrainJob.

Sure, we can have this. Maybe we should create a tracking issue to implement it ?
I don't think is has a high priority compare to other tasks.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, sure. I will open the issue.
But, let me mention the propagation mechanism in this KEP.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

[...]
status:
conditions:
- type: Created
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be helpful to add transition time and also reason for failure within conditions? I believe it would be help debugging failures

Copy link
Member Author

@tenzen-y tenzen-y Oct 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is pseudo conditions. So, we actually support all fields supported in metav1.Conditions including transition time as you can see there:

// Conditions for the TrainJob.
Conditions []metav1.Condition `json:"conditions,omitempty"`

@tenzen-y
Copy link
Member Author

@andreyvelich I addressed all your comments. PTAL, thanks.

Comment on lines +963 to +970
created_choice --> Created=True: Succeeded to build and deploy Jobs.
created_choice --> Created=False: Failed to build and deploy Jobs.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be helpful to add small clarified message what does Failed to build Jobs mean ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

[*] --> created_choice: TrainJob is submitted.
created_choice --> Created=True: Succeeded to build and deploy Jobs.
created_choice --> Created=False: Failed to build and deploy Jobs.
Created=False --> Created=False: Wait for updated appropriate TrainJob.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you mean by Wait for updated appropriate TrainJob ?
I thought if build or deploy Jobs fails, we transition TrainJob to Failed condition ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TrainJob is the mutable object. So, we wait for updating TrainJob with proper fields here.

Copy link
Member

@andreyvelich andreyvelich Oct 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some API in TrainJob is immutable, like managedBy, but the Trainer API is mutable, right ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that's right. We allow users to modify the TrainJob except for some fields like managedBy.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make sense, in that case my question is why do we transition TrainJob in the Failed state if the underlying JobSet is Failed ? Since TrainJob image is mutable, user can override it.
Or the main motivation is that when TrainJob is in Created state, it only can transition to Failed or Succeeded ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in that case my question is why do we transition TrainJob in the Failed state if the underlying JobSet is Failed ? Since TrainJob image is mutable, user can override it.

AFAIK, both batch/v1 Job, and JobSet can not be restarted once those have reached the terminal phase (complete or failed).

@tenzen-y tenzen-y force-pushed the add-trainjob-state-transition branch from cdf9267 to 1cca202 Compare November 1, 2024 19:40
@tenzen-y
Copy link
Member Author

tenzen-y commented Nov 1, 2024

@andreyvelich I addressed all your feedback. PTAL, thanks.

Failed=True --> [*]

#COMPLETION
terminal_choice --> Completed=True: Actual Jobs (e.g., JobSet) completed.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we actually call this condition Succeeded to be consistent with JobSet: https://github.com/kubernetes-sigs/jobset/blob/main/api/jobset/v1alpha2/jobset_types.go#L182-L183 ?

From my understanding Completed: True mens that TrainJob is in Failed or Succeeded state.

Copy link
Member Author

@tenzen-y tenzen-y Nov 1, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Basically, Job and JobSet succeed (.replicatedJobsStatus.succeed), and failed (.replicatedJobsStatus.failed) count are not terminal phase, and those are intermediate one. Additionally those are not guaranteed consistency when Success and Failure criteria are conflicts.

So, instead of success count, we should rely on the .status.terminalState or conditions.

Copy link
Member

@andreyvelich andreyvelich Nov 1, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we going to have Success state for the TrainJob conditions ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TrainJob has only Suspend, Completed, Failed, and Created conditions.

@tenzen-y tenzen-y force-pushed the add-trainjob-state-transition branch from 58d8587 to b81a633 Compare November 2, 2024 20:51
Comment on lines +286 to +287
// TrainJobComplete means that the TrainJob has completed its execution.
TrainJobComplete string = "Complete"
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We use "Complete" condition type name to align with batch/v1 Job for now, then we will open an issue to rename "Completed" to "Complete" in the JobSet side.

@andreyvelich
Copy link
Member

Thanks for the update @tenzen-y!
/lgtm
/approve

Copy link

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: andreyvelich

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@tenzen-y
Copy link
Member Author

tenzen-y commented Nov 2, 2024

All green, thanks!

/hold cancel

@google-oss-prow google-oss-prow bot merged commit 9e46f9d into kubeflow:master Nov 2, 2024
43 checks passed
@tenzen-y tenzen-y deleted the add-trainjob-state-transition branch November 2, 2024 21:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants