Skip to content

Commit 857cbef

Browse files
sergey-koryshevKonstantinTyukalovkirill-ivlev
authored
Refactor logic to drain queues after each task (#4213)
* move draining queues before calling method "Complete" * Revert "BUG 1972388: vsbuild task in YAML build pipeline hangs forever in ADO even though the task has already logged completion (#3979)" This reverts commit b3cf2c0. * implemented logic to drain web console and timeline queues after each task * corrected typo Co-authored-by: Konstantin Tyukalov <[email protected]> * renamed variable "drain" to "shouldDrain" --------- Co-authored-by: Konstantin Tyukalov <[email protected]> Co-authored-by: Kirill Ivlev <[email protected]>
1 parent 4e4f00c commit 857cbef

File tree

3 files changed

+51
-53
lines changed

3 files changed

+51
-53
lines changed

src/Agent.Worker/ExecutionContext.cs

+6
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,12 @@ public TaskResult Complete(TaskResult? result = null, string currentOperation =
295295
this.Warning(StringUtil.Loc("TotalThrottlingDelay", TimeSpan.FromMilliseconds(_totalThrottlingDelayInMilliseconds).TotalSeconds));
296296
}
297297

298+
if (!AgentKnobs.DisableDrainQueuesAfterTask.GetValue(this).AsBoolean())
299+
{
300+
_jobServerQueue.ForceDrainWebConsoleQueue = true;
301+
_jobServerQueue.ForceDrainTimelineQueue = true;
302+
}
303+
298304
_record.CurrentOperation = currentOperation ?? _record.CurrentOperation;
299305
_record.ResultCode = resultCode ?? _record.ResultCode;
300306
_record.FinishTime = DateTime.UtcNow;

src/Agent.Worker/StepsRunner.cs

-20
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
using System.Threading.Tasks;
1111
using Microsoft.TeamFoundation.DistributedTask.Expressions;
1212
using Pipelines = Microsoft.TeamFoundation.DistributedTask.Pipelines;
13-
using Microsoft.VisualStudio.Services.CircuitBreaker;
1413
using Agent.Sdk.Knob;
1514

1615
namespace Microsoft.VisualStudio.Services.Agent.Worker
@@ -35,10 +34,6 @@ public interface IStepsRunner : IAgentService
3534

3635
public sealed class StepsRunner : AgentService, IStepsRunner
3736
{
38-
private IJobServerQueue _jobServerQueue;
39-
40-
private IJobServerQueue JobServerQueue => _jobServerQueue ??= HostContext.GetService<IJobServerQueue>();
41-
4237
// StepsRunner should never throw exception to caller
4338
public async Task RunAsync(IExecutionContext jobContext, IList<IStep> steps)
4439
{
@@ -309,21 +304,6 @@ private async Task RunStepAsync(IStep step, CancellationToken jobCancellationTok
309304
// Complete the step context.
310305
step.ExecutionContext.Section(StringUtil.Loc("StepFinishing", step.DisplayName));
311306
step.ExecutionContext.Complete();
312-
313-
if (!AgentKnobs.DisableDrainQueuesAfterTask.GetValue(step.ExecutionContext).AsBoolean())
314-
{
315-
try
316-
{
317-
// We need to drain the queues after a task just in case if
318-
// there are a lot of items since it can cause some UI hangs.
319-
await JobServerQueue.DrainQueues();
320-
}
321-
catch (Exception ex)
322-
{
323-
Trace.Error($"Error has occurred while draining queues, it can cause some UI glitches but it doesn't affect a pipeline execution itself: {ex}");
324-
step.ExecutionContext.Error(ex);
325-
}
326-
}
327307
}
328308

329309
private async Task SwitchToUtf8Codepage(IStep step)

src/Microsoft.VisualStudio.Services.Agent/JobServerQueue.cs

+45-33
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,10 @@ namespace Microsoft.VisualStudio.Services.Agent
1818
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1711: Identifiers should not have incorrect suffix")]
1919
public interface IJobServerQueue : IAgentService, IThrottlingReporter
2020
{
21+
bool ForceDrainWebConsoleQueue { get; set; }
22+
bool ForceDrainTimelineQueue { get; set; }
2123
event EventHandler<ThrottlingEventArgs> JobServerQueueThrottling;
2224
Task ShutdownAsync();
23-
Task DrainQueues();
2425
void Start(Pipelines.AgentJobRequestMessage jobRequest);
2526
void QueueWebConsoleLine(Guid stepRecordId, string line, long lineNumber);
2627
void QueueFileUpload(Guid timelineId, Guid timelineRecordId, string type, string name, string path, bool deleteSource);
@@ -86,34 +87,15 @@ public sealed class JobServerQueue : AgentService, IJobServerQueue
8687
private bool _writeToBlobStoreAttachments = false;
8788
private bool _debugMode = false;
8889

90+
public bool ForceDrainWebConsoleQueue { get; set; }
91+
public bool ForceDrainTimelineQueue { get; set; }
92+
8993
public override void Initialize(IHostContext hostContext)
9094
{
9195
base.Initialize(hostContext);
9296
_jobServer = hostContext.GetService<IJobServer>();
9397
}
9498

95-
public async Task DrainQueues()
96-
{
97-
// Drain the queue
98-
// ProcessWebConsoleLinesQueueAsync() will never throw exception, live console update is always best effort.
99-
Trace.Verbose("Draining web console line queue.");
100-
await ProcessWebConsoleLinesQueueAsync(runOnce: true);
101-
Trace.Info("Web console line queue drained.");
102-
103-
// ProcessFilesUploadQueueAsync() will never throw exception, log file upload is always best effort.
104-
Trace.Verbose("Draining file upload queue.");
105-
await ProcessFilesUploadQueueAsync(runOnce: true);
106-
Trace.Info("File upload queue drained.");
107-
108-
// ProcessTimelinesUpdateQueueAsync() will throw exception during shutdown
109-
// if there is any timeline records that failed to update contains output variabls.
110-
Trace.Verbose("Draining timeline update queue.");
111-
await ProcessTimelinesUpdateQueueAsync(runOnce: true);
112-
Trace.Info("Timeline update queue drained.");
113-
114-
Trace.Info("All queues are drained.");
115-
}
116-
11799
public void Start(Pipelines.AgentJobRequestMessage jobRequest)
118100
{
119101
Trace.Entering();
@@ -192,7 +174,24 @@ public async Task ShutdownAsync()
192174
_queueInProcess = false;
193175
Trace.Info("All queue process task stopped.");
194176

195-
await DrainQueues();
177+
// Drain the queue
178+
// ProcessWebConsoleLinesQueueAsync() will never throw exception, live console update is always best effort.
179+
Trace.Verbose("Draining web console line queue.");
180+
await ProcessWebConsoleLinesQueueAsync(runOnce: true);
181+
Trace.Info("Web console line queue drained.");
182+
183+
// ProcessFilesUploadQueueAsync() will never throw exception, log file upload is always best effort.
184+
Trace.Verbose("Draining file upload queue.");
185+
await ProcessFilesUploadQueueAsync(runOnce: true);
186+
Trace.Info("File upload queue drained.");
187+
188+
// ProcessTimelinesUpdateQueueAsync() will throw exception during shutdown
189+
// if there is any timeline records that failed to update contains output variables.
190+
Trace.Verbose("Draining timeline update queue.");
191+
await ProcessTimelinesUpdateQueueAsync(runOnce: true);
192+
Trace.Info("Timeline update queue drained.");
193+
194+
Trace.Info("All queue process tasks have been stopped, and all queues are drained.");
196195
}
197196

198197
public void QueueWebConsoleLine(Guid stepRecordId, string line, long lineNumber)
@@ -248,6 +247,12 @@ private async Task ProcessWebConsoleLinesQueueAsync(bool runOnce = false)
248247
{
249248
while (!_jobCompletionSource.Task.IsCompleted || runOnce)
250249
{
250+
bool shouldDrain = ForceDrainWebConsoleQueue;
251+
if (ForceDrainWebConsoleQueue)
252+
{
253+
ForceDrainWebConsoleQueue = false;
254+
}
255+
251256
if (_webConsoleLineAggressiveDequeue && ++_webConsoleLineAggressiveDequeueCount > _webConsoleLineAggressiveDequeueLimit)
252257
{
253258
Trace.Info("Stop aggressive process web console line queue.");
@@ -279,7 +284,7 @@ private async Task ProcessWebConsoleLinesQueueAsync(bool runOnce = false)
279284
// process at most about 500 lines of web console line during regular timer dequeue task.
280285
// Send the first line of output to the customer right away
281286
// It might take a while to reach 500 line outputs, which would cause delays before customers see the first line
282-
if ((!runOnce && linesCounter > 500) || _firstConsoleOutputs)
287+
if ((!runOnce && !shouldDrain && linesCounter > 500) || _firstConsoleOutputs)
283288
{
284289
break;
285290
}
@@ -314,7 +319,7 @@ private async Task ProcessWebConsoleLinesQueueAsync(bool runOnce = false)
314319
// We batch and produce 500 lines of web console output every 500ms
315320
// If customer's task produce massive of outputs, then the last queue drain run might take forever.
316321
// So we will only upload the last 200 lines of each step from all buffered web console lines.
317-
if (runOnce && batchedLines.Count > 2)
322+
if ((runOnce || shouldDrain) && batchedLines.Count > 2)
318323
{
319324
Trace.Info($"Skip {batchedLines.Count - 2} batches web console lines for last run");
320325
batchedLines = batchedLines.TakeLast(2).ToList();
@@ -430,6 +435,8 @@ private async Task ProcessTimelinesUpdateQueueAsync(bool runOnce = false)
430435
{
431436
while (!_jobCompletionSource.Task.IsCompleted || runOnce)
432437
{
438+
bool shouldDrain = ForceDrainTimelineQueue;
439+
433440
List<PendingTimelineRecord> pendingUpdates = new List<PendingTimelineRecord>();
434441
foreach (var timeline in _allTimelines)
435442
{
@@ -442,7 +449,7 @@ private async Task ProcessTimelinesUpdateQueueAsync(bool runOnce = false)
442449
{
443450
records.Add(record);
444451
// process at most 25 timeline records update for each timeline.
445-
if (!runOnce && records.Count > 25)
452+
if (!runOnce && !shouldDrain && records.Count > 25)
446453
{
447454
break;
448455
}
@@ -514,7 +521,7 @@ private async Task ProcessTimelinesUpdateQueueAsync(bool runOnce = false)
514521
}
515522
}
516523

517-
if (runOnce)
524+
if (runOnce || shouldDrain)
518525
{
519526
// continue process timeline records update,
520527
// we might have more records need update,
@@ -535,14 +542,19 @@ private async Task ProcessTimelinesUpdateQueueAsync(bool runOnce = false)
535542
}
536543
else
537544
{
538-
break;
545+
if (ForceDrainTimelineQueue)
546+
{
547+
ForceDrainTimelineQueue = false;
548+
}
549+
if (runOnce)
550+
{
551+
break;
552+
}
539553
}
540554
}
541555
}
542-
else
543-
{
544-
await Task.Delay(_delayForTimelineUpdateDequeue);
545-
}
556+
557+
await Task.Delay(_delayForTimelineUpdateDequeue);
546558
}
547559
}
548560

0 commit comments

Comments
 (0)