Skip to content

Commit c908116

Browse files
committed
Fix some subtle invariants around needing new projectContextIds, especially for cached project contexts
1 parent ce31731 commit c908116

File tree

2 files changed

+35
-20
lines changed

2 files changed

+35
-20
lines changed

src/Build/BackEnd/Components/Logging/LoggingServiceLogMethods.cs

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -527,18 +527,27 @@ private int GenerateNewProjectContextId(string projectFile)
527527
/// <summary>
528528
/// Validates that a preexisting projectContextId is valid and updates the project file map to track which contexts apply to which project files.
529529
/// </summary>
530-
/// <param name="projectContextId">The preexisting project context ID to validate.</param>
530+
/// <param name="remoteNodeEvaluationContext">The remote node evaluation context containing the project context ID to validate.</param>
531531
/// <param name="projectFile">The project file path to be associated with this project context ID.</param>
532532
/// <param name="currentNodeId">The node ID of the current node - used to validate that only the in-proc scheduler node is re-using cached project context IDs.</param>
533-
private void ValidatePreexistingProjectContextId(int projectContextId, string projectFile, int currentNodeId)
533+
/// <returns>Either the same context, if the projectContextId exists in the map already, or a new context with a generated projectContextId if it did not.</returns>
534+
private BuildEventContext ValidatePreexistingProjectContextId(BuildEventContext remoteNodeEvaluationContext, string projectFile, int currentNodeId)
534535
{
536+
if (remoteNodeEvaluationContext.ProjectContextId == BuildEventContext.InvalidProjectContextId)
537+
{
538+
// No projectContextId was provided, so generate a new one to represent this invocation of the project
539+
int newProjectContextId = GenerateNewProjectContextId(projectFile);
540+
return remoteNodeEvaluationContext.WithProjectContextId(newProjectContextId);
541+
}
542+
535543
// A projectContextId was provided, so use it with some sanity checks
536-
if (_projectFileMap.TryGetValue(projectContextId, out string existingProjectFile))
544+
else if (_projectFileMap.TryGetValue(remoteNodeEvaluationContext.ProjectContextId, out string existingProjectFile))
537545
{
538546
if (!projectFile.Equals(existingProjectFile, StringComparison.OrdinalIgnoreCase))
539547
{
540-
ErrorUtilities.ThrowInternalError("ContextID {0} was already in the ID-to-project file mapping but the project file {1} did not match the provided one {2}!", projectContextId, existingProjectFile, projectFile);
548+
ErrorUtilities.ThrowInternalError("ContextID {0} was already in the ID-to-project file mapping but the project file {1} did not match the provided one {2}!", remoteNodeEvaluationContext.ProjectContextId, existingProjectFile, projectFile);
541549
}
550+
return remoteNodeEvaluationContext;
542551
}
543552
else
544553
{
@@ -547,10 +556,11 @@ private void ValidatePreexistingProjectContextId(int projectContextId, string pr
547556
// So we only need this sanity check for the in-proc node.
548557
if (currentNodeId == Scheduler.InProcNodeId)
549558
{
550-
ErrorUtilities.ThrowInternalError("ContextID {0} should have been in the ID-to-project file mapping but wasn't!", projectContextId);
559+
ErrorUtilities.ThrowInternalError("ContextID {0} should have been in the ID-to-project file mapping but wasn't!", remoteNodeEvaluationContext.ProjectContextId);
551560
}
552561

553-
_projectFileMap[projectContextId] = projectFile;
562+
_projectFileMap[remoteNodeEvaluationContext.ProjectContextId] = projectFile;
563+
return remoteNodeEvaluationContext;
554564
}
555565
}
556566

@@ -566,16 +576,18 @@ public ProjectStartedEventArgs CreateProjectStartedForCachedProject(
566576
{
567577
Debug.Assert(currentNodeBuildEventContext.NodeId == Scheduler.InProcNodeId, "Cached project build events should only be logged on the in-proc scheduler node.");
568578

569-
ValidatePreexistingProjectContextId(
570-
remoteNodeEvaluationBuildEventContext.ProjectContextId,
579+
BuildEventContext validatedRemoteNodeEvaluationBuildContext = ValidatePreexistingProjectContextId(
580+
remoteNodeEvaluationBuildEventContext,
571581
projectFile,
572582
currentNodeBuildEventContext.NodeId);
573583

574584
int configurationId = remoteNodeEvaluationBuildEventContext.ProjectInstanceId;
575585

586+
// we need to be a valid BuildEventContext for a ProjectLoggingContext out of this, so we need to make sure
587+
// we have ProjectContextId set.
576588
BuildEventContextBuilder thisEventBuildEventContext = parentBuildEventContext
577589
.WithProjectInstanceId(configurationId)
578-
.WithProjectContextId(remoteNodeEvaluationBuildEventContext.ProjectContextId);
590+
.WithProjectContextId(validatedRemoteNodeEvaluationBuildContext.ProjectContextId);
579591

580592
ProjectStartedEventArgs buildEvent = new(
581593
projectId: configurationId,
@@ -588,7 +600,7 @@ public ProjectStartedEventArgs CreateProjectStartedForCachedProject(
588600
parentBuildEventContext: parentBuildEventContext,
589601
globalProperties: globalProperties,
590602
toolsVersion: toolsVersion,
591-
originalBuildEventContext: remoteNodeEvaluationBuildEventContext)
603+
originalBuildEventContext: validatedRemoteNodeEvaluationBuildContext)
592604
{
593605
BuildEventContext = thisEventBuildEventContext,
594606
};

src/Build/BackEnd/Components/Logging/ProjectLoggingContext.cs

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -66,9 +66,13 @@ public static (ProjectStartedEventArgs, ProjectLoggingContext) CreateForLocalBui
6666
requestEntry.RequestConfiguration.Project?.ItemsToBuildWith);
6767

6868
IDictionary<string, string> globalProperties = requestEntry.RequestConfiguration.GlobalProperties.ToDictionary();
69-
69+
70+
BuildEventContext parentBuildEventContext = requestEntry.Request.ParentBuildEventContext == BuildEventContext.Invalid
71+
? nodeLoggingContext.BuildEventContext.WithEvaluationId(requestEntry.RequestConfiguration.ProjectEvaluationId).WithSubmissionId(requestEntry.Request.SubmissionId)
72+
: requestEntry.Request.ParentBuildEventContext;
73+
7074
ProjectStartedEventArgs args = nodeLoggingContext.LoggingService.CreateProjectStartedForLocalProject(
71-
requestEntry.Request.ParentBuildEventContext,
75+
parentBuildEventContext,
7276
requestEntry.RequestConfiguration.ConfigurationId,
7377
requestEntry.RequestConfiguration.ProjectFullPath,
7478
string.Join(";", requestEntry.Request.Targets),
@@ -112,28 +116,27 @@ public static ProjectLoggingContext CreateForCacheBuild(
112116
/// </summary>
113117
private static BuildEventContext CreateAndLogProjectStartedForCache(
114118
NodeLoggingContext nodeLoggingContext,
115-
BuildRequest request,
119+
BuildRequest newRequestThatWasServedFromCache,
116120
BuildRequestConfiguration configuration)
117121
{
118122
// Create a remote node evaluation context with the original evaluation ID
119123
BuildEventContext remoteNodeEvaluationBuildEventContext = BuildEventContext.CreateInitial(
120124
configuration.ResultsNodeId, // Use the node that originally built this project configuration
121-
request.SubmissionId)
125+
newRequestThatWasServedFromCache.SubmissionId)
122126
.WithEvaluationId(configuration.ProjectEvaluationId)
123-
.WithProjectInstanceId(configuration.ConfigurationId)
124-
// TODO: should we keep this data on the 'original' context - it is _very likely_ not the project
125-
// context Id of the request that created the cached result, but it is the only data we have at this point (so far)
126-
.WithProjectContextId(request.ProjectContextId);
127+
.WithProjectInstanceId(configuration.ConfigurationId);
128+
// we don't know the projectContextId of the remote eval, so we don't set it at all.
129+
// the new request _does not have_ a valid projectContextId to go off of.
127130

128131
IDictionary<string, string> globalProperties = configuration.GlobalProperties.ToDictionary();
129132

130133
ProjectStartedEventArgs args = nodeLoggingContext.LoggingService.CreateProjectStartedForCachedProject(
131134
nodeLoggingContext.BuildEventContext, // Current node context
132135
remoteNodeEvaluationBuildEventContext, // Original remote node context
133-
request.ParentBuildEventContext,
136+
newRequestThatWasServedFromCache.ParentBuildEventContext,
134137
globalProperties,
135138
configuration.ProjectFullPath,
136-
string.Join(";", request.Targets),
139+
string.Join(";", newRequestThatWasServedFromCache.Targets),
137140
configuration.ToolsVersion);
138141

139142
nodeLoggingContext.LoggingService.LogProjectStarted(args);

0 commit comments

Comments
 (0)