// This method runs concurrently with other index processing. // Ensure all logic here is idempotent. public void ProcessFunctionStarted(FunctionStartedMessage message) { FunctionInstanceSnapshot snapshot = CreateSnapshot(message); _functionInstanceLogger.LogFunctionStarted(snapshot); string functionId = new FunctionIdentifier(message.SharedQueueName, message.Function.Id).ToString(); Guid functionInstanceId = message.FunctionInstanceId; DateTimeOffset startTime = message.StartTime; WebJobRunIdentifier webJobRunId = message.WebJobRunIdentifier; Guid? parentId = message.ParentId; // Race to write index entries for function started. if (!HasLoggedFunctionCompleted(functionInstanceId)) { CreateOrUpdateIndexEntries(snapshot, startTime, webJobRunId); } // If the function has since completed, we lost the race. // Delete any function started index entries. // Note that this code does not depend on whether or not the index entries were previously written by this // method, as this processing could have been aborted and resumed at another point in time. In that case, // we still own cleaning up any dangling function started index entries. DateTimeOffset?functionCompletedTime = GetFunctionCompletedTime(functionInstanceId); bool hasLoggedFunctionCompleted = functionCompletedTime.HasValue; if (hasLoggedFunctionCompleted) { DeleteFunctionStartedIndexEntriesIfNeeded(functionInstanceId, message.StartTime, functionCompletedTime.Value, functionId, parentId, webJobRunId); } }