Skip to content

Commit b794503

Browse files
andystaplesCopilot
andcommitted
Reject conflicting Functions registrations
Keep repeated invocation idempotent only for the original callable and preserve duplicate-registration errors when a shared worker receives a different function with the same name. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: b29a8f40-77ba-48d9-a3f7-2748491f3791
1 parent aa39da0 commit b794503

2 files changed

Lines changed: 50 additions & 4 deletions

File tree

‎azure-functions-durable/azure/durable_functions/worker.py‎

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,12 @@ def __init__(self) -> None:
4343
# the wire format the Durable Functions host extension expects.
4444
super().__init__(data_converter=DEFAULT_FUNCTIONS_DATA_CONVERTER)
4545
self._registration_lock = Lock()
46+
self._registered_orchestrator_functions: dict[
47+
str, task.Orchestrator[Any, Any]
48+
] = {}
49+
self._registered_entity_functions: dict[
50+
str, task.Entity[Any, Any]
51+
] = {}
4652

4753
def add_named_orchestrator(self, name: str, func: task.Orchestrator[Any, Any]) -> None:
4854
self._registry.add_named_orchestrator(name, func)
@@ -53,14 +59,22 @@ def _register_orchestrator_once(
5359
func: task.Orchestrator[Any, Any],
5460
) -> None:
5561
with self._registration_lock:
56-
if self._registry.get_orchestrator(name) is None:
57-
self.add_named_orchestrator(name, wrap_orchestrator(func))
62+
if self._registered_orchestrator_functions.get(name) is func:
63+
return
64+
if self._registry.get_orchestrator(name) is not None:
65+
raise ValueError(f"A '{name}' orchestrator already exists.")
66+
self.add_named_orchestrator(name, wrap_orchestrator(func))
67+
self._registered_orchestrator_functions[name] = func
5868

5969
def _register_entity_once(self, func: task.Entity[Any, Any]) -> None:
6070
name = task.get_entity_name(func).lower()
6171
with self._registration_lock:
62-
if self._registry.get_entity(name) is None:
63-
self.add_entity(wrap_entity(func), name=name)
72+
if self._registered_entity_functions.get(name) is func:
73+
return
74+
if self._registry.get_entity(name) is not None:
75+
raise ValueError(f"A '{name}' entity already exists.")
76+
self.add_entity(wrap_entity(func), name=name)
77+
self._registered_entity_functions[name] = func
6478

6579
def execute_orchestration_request(self, func: task.Orchestrator[Any, Any], context: Any) -> str:
6680
context_body = getattr(context, "body", None)

‎tests/azure-functions-durable/test_worker_compat.py‎

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,21 @@ def orchestrator(context):
151151
)
152152

153153

154+
def test_execute_orchestration_request_rejects_different_function_with_same_name():
155+
def first_orchestrator(context):
156+
return "first"
157+
158+
def second_orchestrator(context):
159+
return "second"
160+
161+
worker = DurableFunctionsWorker()
162+
encoded = _encode_orchestrator_request("same-name")
163+
worker.execute_orchestration_request(first_orchestrator, encoded)
164+
165+
with pytest.raises(ValueError, match="A 'same-name' orchestrator already exists"):
166+
worker.execute_orchestration_request(second_orchestrator, encoded)
167+
168+
154169
# ---------------------------------------------------------------------------
155170
# execute_entity_batch_request
156171
# ---------------------------------------------------------------------------
@@ -237,3 +252,20 @@ def entity(context):
237252
result).results[0].success.result.value) == "handled"
238253
for result in results
239254
)
255+
256+
257+
def test_execute_entity_batch_request_rejects_different_function_with_same_name():
258+
def first_entity(context):
259+
context.set_result("first")
260+
261+
def second_entity(context):
262+
context.set_result("second")
263+
264+
first_entity.__durable_entity_name__ = "Counter"
265+
second_entity.__durable_entity_name__ = "Counter"
266+
worker = DurableFunctionsWorker()
267+
encoded = _encode_entity_batch_request("@counter@key1", "op")
268+
worker.execute_entity_batch_request(first_entity, encoded)
269+
270+
with pytest.raises(ValueError, match="A 'counter' entity already exists"):
271+
worker.execute_entity_batch_request(second_entity, encoded)

0 commit comments

Comments
 (0)