-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathConsoleEnumerator.cs
159 lines (140 loc) · 5.52 KB
/
ConsoleEnumerator.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
using NLog;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading;
using Jira2AzureDevOps.Logic.Framework;
namespace Jira2AzureDevOps.Console.Framework
{
internal static class ConsoleEnumerator
{
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
internal static void EnumerateOperation<T>(this IEnumerable<T> items,
int totalCount,
string operationName,
CancellationToken cancellationToken,
Action<T> action)
{
items.EnumerateOperation(new State(totalCount), operationName, null, null, cancellationToken, action);
}
internal static void EnumerateOperation<T>(this IEnumerable<T> items,
int totalCount,
string operationName,
Func<T, object> getId,
CancellationToken cancellationToken,
Action<T> action)
{
items.EnumerateOperation(new State(totalCount), operationName, getId, null, cancellationToken, action);
}
internal static void EnumerateOperation<T>(this IEnumerable<T> items,
int totalCount,
string operationName,
FileInfo failFile,
CancellationToken cancellationToken,
Action<T> action)
{
items.EnumerateOperation(new State(totalCount), operationName, null, failFile, cancellationToken, action);
}
internal static void EnumerateOperation<T>(this IEnumerable<T> items,
State state,
string operationName,
Func<T, object> getId,
FileInfo failFile,
CancellationToken cancellationToken,
Action<T> action)
{
getId = getId ?? (item => item);
var etaCalculator = new EtaCalculator(5, maximumDuration: TimeSpan.FromMinutes(2).Ticks, state.TotalCount);
var totalTime = Stopwatch.StartNew();
Logger.Info("begin {operationName} for {count} items", operationName, state.TotalCount);
foreach (var item in items.TakeWhile(i => !state.ShouldQuit && !cancellationToken.IsCancellationRequested))
{
var id = getId(item);
try
{
state.Processed++;
etaCalculator.Increment();
action(item);
Logger.Debug("processed {operationName} for {id}", operationName, id);
}
catch (Exception e)
{
state.Errored++;
Logger.Error(e, "errored {operationName} for {id}", operationName, id);
failFile?.AppendAllLines(id.ToString().ToEnumerable());
}
finally
{
if (state.ShouldReport())
{
if (etaCalculator.TryGetEta(out var etr, out var eta))
{
Logger.Info(new
{
state = new {state.TotalCount, state.Processed, state.Succeeded, state.Errored},
elapsed = totalTime.Elapsed,
etr, eta
});
}
else
{
Logger.Info(new
{
state = new {state.TotalCount, state.Processed, state.Succeeded, state.Errored},
elapsed = totalTime.Elapsed
});
}
}
}
}
Logger.Info(
state.ShouldQuit ? "quit {operationName}" : "completed {operationName}",
operationName);
if (state.Errored == 0)
{
failFile = null;
}
Logger.Info(new
{
state = new {state.TotalCount, state.Processed, state.Succeeded, state.Errored},
elapsed = totalTime.Elapsed,
failFile
});
}
internal class State
{
private readonly int _minReportCount;
private readonly TimeSpan _minReportTimeSpan;
private int _nextReportCount;
private DateTime _nextReportTime;
public int TotalCount { get; }
public int Processed { get; set; }
public int Errored { get; set; }
public int Succeeded => Processed - Errored;
public bool ShouldQuit { get; private set; }
public State(int totalCount, int minReportCount = 10, TimeSpan? minReportTimeSpan = null)
{
TotalCount = totalCount;
_minReportCount = minReportCount;
_minReportTimeSpan = minReportTimeSpan ?? TimeSpan.FromMinutes(1);
}
public void Quit() => ShouldQuit = true;
internal bool ShouldReport()
{
if (Processed >= _nextReportCount || DateTime.Now >= _nextReportTime)
{
Reported();
return true;
}
return false;
}
private void Reported()
{
_nextReportCount = Processed + _minReportCount;
_nextReportTime = DateTime.Now.Add(_minReportTimeSpan);
}
}
}
}