Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@chriselion
Copy link
Contributor

@chriselion chriselion commented Oct 23, 2025

Resolves #9394

When an execution with the same name is found, it now compares the inputs:

  • If the inputs are different, an ExecutionAlreadyExists exception is raised (same as before)
  • If the inputs are the same, the original execution is returned, instead of creating a new one.

I updated the existing test case to pass an input, and added another test to check that duplicating the name and input is idempotent.


I'm not sure how we should define "equal" inputs here. StateMachine.start_execution() saves the json-decoded input on the Execution

execution = Execution(
region_name=region_name,
account_id=account_id,
state_machine_name=self.name,
execution_name=execution_name,
state_machine_arn=self.arn,
execution_input=json.loads(execution_input),

although the field is supposed to be a str
class Execution:
def __init__(
self,
region_name: str,
account_id: str,
state_machine_name: str,
execution_name: str,
state_machine_arn: str,
execution_input: str,

Since the original input is lost, I'm comparing the json-decoded values.

I can do a little more digging to see what AWS actually does (although if you have some intuition, that would save me some time). If we need to check the exact string equality instead, that'll take some more changes.

Comment on lines 576 to 582
input='{"a": "b", "c": "d"}',
)
#
execution_two = client.start_execution(
stateMachineArn=sm["stateMachineArn"],
name="execution_name",
input='{"c": "d", "a": "b"}',
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Here's a more concrete example of what I meant in the description - are these inputs equal or not?

@codecov
Copy link

codecov bot commented Oct 23, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 93.00%. Comparing base (9761b38) to head (86fe368).

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #9397      +/-   ##
==========================================
- Coverage   93.00%   93.00%   -0.01%     
==========================================
  Files        1294     1294              
  Lines      115774   115780       +6     
==========================================
+ Hits       107672   107677       +5     
- Misses       8102     8103       +1     
Flag Coverage Δ
servertests 28.70% <12.50%> (-0.01%) ⬇️
unittests 92.97% <100.00%> (-0.01%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@bblommers
Copy link
Collaborator

bblommers commented Oct 23, 2025

Hi @chriselion , thanks for raising the PR!

although if you have some intuition, that would save me some time

No, I'm afraid not - I would (also) have to do a bunch of manual testing to determine the exact behavior.

This does look like a tricky scenario, so it does require some more research IMO.

I'd be happy to do so, I just can't guarantee a timeline when that would happen. Let me know what you prefer.

(Edit: also marking this PR as a draft, to make it clear it's not quite ready yet.)

@bblommers bblommers marked this pull request as draft October 23, 2025 08:07
@chriselion
Copy link
Contributor Author

chriselion commented Oct 23, 2025

I ran this script

import boto3
import json
import os

definition = {
  "StartAt": "Wait",
  "States": {
    "Wait": {
      "Type": "Wait",
      "Seconds": 60,
      "End": True
    }
  },
  "QueryLanguage": "JSONata"
}

input1 = '{"a": "b", "c": "d"}'
input2 = '{"c": "d", "a": "b"}'
execution_name = "testExecution"
assert json.loads(input1) == json.loads(input2)


client = boto3.client("stepfunctions")

print("creating state machine")
resp = client.create_state_machine(
    name="motoRepro1",
    definition=json.dumps(definition),
    roleArn=os.getenv("ROLE_ARN")
)
state_machine_arn = resp["stateMachineArn"]

try:
    execution_response = client.start_execution(
        stateMachineArn=state_machine_arn,
        name=execution_name,
        input=input1,
    )
    execution_arn = execution_response["executionArn"]
    print("Initial execution successful")
except:
    print("Initial execution failed")

try:
    identical_response = client.start_execution(
        stateMachineArn=state_machine_arn,
        name=execution_name,
        input=input1,
    )
    print("Identical execution successful")
    print(f"{(execution_arn==identical_response["executionArn"])=}")
except:
    print("Identical execution failed")

try:
    equivalent_response = client.start_execution(
        stateMachineArn=state_machine_arn,
        name=execution_name,
        input=input2,
    )
    print("Equivalent execution successful")
    print(f"{(execution_arn==equivalent_response["executionArn"])}")
except:
    print("Equivalent execution failed")

print("deleting state machine")
client.delete_state_machine(stateMachineArn=state_machine_arn)

which produces this output:

creating state machine
Initial execution successful
Identical execution successful
(execution_arn==identical_response["executionArn"])=True
Equivalent execution failed
deleting state machine

So I'll update Execution to store the string form of the execution_input (which is what the type hint indicates anyway). Hopefully I can get to that today.

@chriselion
Copy link
Contributor Author

I tried this tonight, but it's more complicated than I expected. I removed the json.load() call in start_execution() and figured I'd need to remove the corresponding json.dumps() here

"input": json.dumps(execution.execution_input),

but that ended up breaking a lot of tests.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

SFN.Client.start_execution() doesn't follow same idempotency guarantees as boto3

2 participants