Azure / Azure/azure-functions-host
Foreground SyncTriggers publish trigger metadata but never update the hash blob, breaking self-heal
- Dominant language
- C#
- Stars
- 2k
- Forks
- 482
- Avg merge
- 2d 10h
- Merged PRs (30d)
- 36
Description
Split out from #11873 (suggested fix **#2**).
### Summary
`FunctionsSyncManager.TrySyncTriggersAsync` only reads and writes the SyncTriggers hash blob (`synctriggers/{hostId}/last`) when `isBackgroundSync == true`. **Foreground** syncs — inbound `/admin/host/synctriggers` requests — call `SetTriggersAsync` and publish the trigger payload to the platform, but they **never check, update, or invalidate the hash blob**. This leaves the hash blob out of sync with what was actually last published, which defeats the background-sync self-heal mechanism.
### Details
In `TrySyncTriggersAsync` ([`FunctionsSyncManager.cs`](https://github.com/Azure/azure-functions-host/blob/dev/src/WebJobs.Script.WebHost/Management/FunctionsSyncManager.cs#L99-L160)):
- The hash is only consulted when `isBackgroundSync` is `true`:
```csharp
bool shouldSyncTriggers = true;
string newHash = null;
if (isBackgroundSync && hashBlobClient != null)
{
newHash = await CheckHashAsync(hashBlobClient, payload.Content);
shouldSyncTriggers = newHash != null;
}
if (shouldSyncTriggers)
{
var (success, error) = await SetTriggersAsync(payload.Content);
if (success && newHash != null) // newHash is always null for foreground syncs
{
await UpdateHashAsync(hashBlobClient, newHash);
}
...
}
```
- For a foreground sync, `newHash` stays `null`, so `SetTriggersAsync` publishes the payload but `UpdateHashAsync` is never called.
### Why this is a bug
Because a foreground sync can publish a payload that differs from the hash currently stored in the blob, the blob no longer reflects the last-published trigger metadata. A subsequent **background** sync recomputes the current hash, finds it *matches* the (now-stale) blob, concludes "nothing changed," and **suppresses the corrective re-publish** — even though the live platform view is wrong.
This is the mechanism that wedges an app on the placeholder `WarmUp` payload in #11873:
1. A healthy **background** sync writes the *real-functions* hash to the blob.
2. A bad **foreground** `WarmUp` publish overwrites the live trigger payload but leaves the blob unchanged (still the real-functions hash).
3. The host recovers, recomputes the real-functions hash, compares to the blob → **they match** → background sync suppresses the re-publish.
Result: the platform stays on stale/incorrect trigger metadata until an external forced sync (portal Refresh, ARM `host/default/sync`, or another deploy) happens to re-publish.
### Proposed fix
Make foreground syncs keep the hash blob consistent with what they publish, so background self-heal can still detect drift. Two options:
- **Update** the hash blob after a successful foreground `SetTriggersAsync` (compute and store the published payload's hash), or
- **Invalidate/delete** the hash blob on foreground publish, forcing the next background sync to re-publish.
Updating is preferable to invalidating so we don't trigger an unnecessary extra publish, but either restores the self-heal guarantee that the blob always reflects the last-published payload.
### Related
- #11873 (parent — placeholder `WarmUp` stuck state)
- #10169
- https://github.com/Azure/functions-action/issues/245
Contributor guide
Research direction
Start in src/WebJobs.Script.WebHost/Management/FunctionsSyncManager.cs, especially TrySyncTriggersAsync and its foreground SetTriggersAsync path. Trace how the hash blob is checked and updated, then verify that a successful foreground publish leaves the blob consistent with the published payload so a later background sync can detect drift.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- azure, csharp
- Domain
- api, backend
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 72/100