257. Workflow Step State Transitions History
Asked in
Workflow Step State Transitions History
Design a component that records state changes for steps inside workflows. Each workflow has multiple step ids, and every step starts in the Pending state.
A step can move from Pending to Running, and then from Running to either Completed or Failed. The component must keep the complete transition history for every step after workflow creation.
The initial Pending state at workflow creation is not stored as a transition. Only successful state updates are stored in transition history.

Class

WorkflowStateLogger

Methods

Create Workflow

String createWorkflow(String workflowId, List<String> stepIds, long currentTimestamp)
  • Creates a workflow with the given step ids.
  • All steps start in state Pending.
  • Returns "CREATED" if the workflow is created successfully.
  • Returns "DUPLICATE_WORKFLOW" if workflowId already exists.
  • Returns "DUPLICATE_STEP" if the same step id appears more than once in stepIds.

Update Step State

String updateStepState(String workflowId, String stepId, String newState, long currentTimestamp)
  • Updates the state of one step and records the transition.
  • Valid states are Pending, Running, Completed, and Failed.
  • Returns "UPDATED" if the transition is valid and recorded.
  • Returns "INVALID_WORKFLOW" if the workflow does not exist.
  • Returns "INVALID_STEP" if the step does not exist in that workflow.
  • Returns "INVALID_TRANSITION" if the state change is not allowed.

Get Step History

List<String> getStepHistory(String workflowId, String stepId, long currentTimestamp)
  • Returns all recorded transitions for the given step in increasing currentTimestamp order.
  • Each transition is returned as a string in the format "currentTimestamp,oldState,newState".
  • If two transitions have the same currentTimestamp, they are returned in the order in which they were recorded.
  • Returns a list containing "INVALID_WORKFLOW" if the workflow does not exist.
  • Returns a list containing "INVALID_STEP" if the step does not exist in that workflow.
  • Returns an empty list if the step exists but has no recorded transition yet.

Get Workflow Snapshot

List<String> getWorkflowSnapshot(String workflowId, long currentTimestamp)
  • Returns the latest state of every step in the workflow.
  • Each step is returned as a string in the format "stepId,state,lastUpdatedCurrentTimestamp".
  • The result is sorted by step id in lexicographical order using normal case-sensitive string ordering.
  • For a step that was never updated, lastUpdatedCurrentTimestamp is the workflow creation currentTimestamp.
  • Returns a list containing "INVALID_WORKFLOW" if the workflow does not exist.

Transition Rules

  • Pending can move only to Running.
  • Running can move only to Completed or Failed.
  • Completed cannot move to any other state.
  • Failed cannot move to any other state.
  • Updating a step to its current state is treated as "INVALID_TRANSITION".
  • Pending is a valid state name, but no update operation can transition a step into Pending.
  • State names are case-sensitive.

Error Priority

  • For createWorkflow: DUPLICATE_WORKFLOW, DUPLICATE_STEP.
  • For updateStepState: INVALID_WORKFLOW, INVALID_STEP, INVALID_TRANSITION.
  • For getStepHistory: INVALID_WORKFLOW, INVALID_STEP.
  • For getWorkflowSnapshot: INVALID_WORKFLOW.

Current Timestamp Rules

  • currentTimestamp will always be monotonically increasing, meaning non-decreasing, across all method calls.
  • Only successful createWorkflow and updateStepState operations update stored workflow state, step state, transition history, and last updated current timestamp.
  • Getter methods receive currentTimestamp only to preserve global call ordering. They do not change stored workflow state, step state, transition history, or last updated current timestamp.
  • Unsuccessful operations do not update stored workflow state, step state, transition history, or last updated current timestamp.

Constraints

  • All input values are valid and non-empty unless the operation is testing one of the listed logical error cases.
  • 1 ≤ workflowId.length() ≤ 100
  • 1 ≤ stepIds.size() ≤ 1,000
  • 1 ≤ stepId.length() ≤ 100
  • 0 ≤ currentTimestamp ≤ 1,000,000,000
  • newState will always be one of Pending, Running, Completed, or Failed.
  • All strings contain only lowercase letters, uppercase letters, digits, hyphen, and underscore.

Examples

Example 1

WorkflowStateLogger logger = new WorkflowStateLogger()
logger.createWorkflow(workflowId = "order_1", stepIds = List.of("charge", "pack", "ship"), currentTimestamp = 10)
Output: "CREATED"
logger.updateStepState(workflowId = "order_1", stepId = "pack", newState = "Running", currentTimestamp = 15)
Output: "UPDATED"
logger.updateStepState(workflowId = "order_1", stepId = "pack", newState = "Completed", currentTimestamp = 20)
Output: "UPDATED"
logger.getStepHistory(workflowId = "order_1", stepId = "pack", currentTimestamp = 25)
Output: List.of("15,Pending,Running", "20,Running,Completed")

Example 2

WorkflowStateLogger logger = new WorkflowStateLogger()
logger.createWorkflow(workflowId = "deploy_7", stepIds = List.of("build", "test"), currentTimestamp = 100)
Output: "CREATED"
logger.updateStepState(workflowId = "deploy_7", stepId = "build", newState = "Completed", currentTimestamp = 110)
Output: "INVALID_TRANSITION"
logger.updateStepState(workflowId = "deploy_7", stepId = "build", newState = "Running", currentTimestamp = 115)
Output: "UPDATED"
logger.getWorkflowSnapshot(workflowId = "deploy_7", currentTimestamp = 120)
Output: List.of("build,Running,115", "test,Pending,100")

Example 3

WorkflowStateLogger logger = new WorkflowStateLogger()
logger.createWorkflow(workflowId = "job_5", stepIds = List.of("extract", "load"), currentTimestamp = 50)
Output: "CREATED"
logger.updateStepState(workflowId = "job_5", stepId = "extract", newState = "Running", currentTimestamp = 60)
Output: "UPDATED"
logger.updateStepState(workflowId = "job_5", stepId = "extract", newState = "Failed", currentTimestamp = 65)
Output: "UPDATED"
logger.updateStepState(workflowId = "job_5", stepId = "extract", newState = "Running", currentTimestamp = 70)
Output: "INVALID_TRANSITION"
logger.getStepHistory(workflowId = "job_5", stepId = "extract", currentTimestamp = 75)
Output: List.of("60,Pending,Running", "65,Running,Failed")


Please use Laptop/Desktop or any other large screen to add/edit code.