11
11
using System . Collections ;
12
12
using System . Collections . Generic ;
13
13
using System . Collections . Immutable ;
14
+ using System . Collections . ObjectModel ;
15
+ using System . Diagnostics . CodeAnalysis ;
14
16
using System . Linq ;
15
17
using System . Text ;
16
18
using System . Threading . Tasks ;
@@ -33,6 +35,8 @@ private record ClusterCache(
33
35
private class ClusterQueue
34
36
{
35
37
private readonly object _lock = new object ( ) ;
38
+ private readonly PriorityQueue < RowItem , KustoPriority > _exportQueue ;
39
+ private readonly IDictionary < string , RowItem > _operationIdToBlockMap ;
36
40
private readonly Func < CancellationToken , Task < ClusterCache > > _clusterCacheFetch ;
37
41
private Task < ClusterCache > ? _cacheTask ;
38
42
@@ -41,15 +45,11 @@ public ClusterQueue(
41
45
IDictionary < string , RowItem > operationIdToBlockMap ,
42
46
Func < CancellationToken , Task < ClusterCache > > clusterCacheFetch )
43
47
{
44
- ExportQueue = exportQueue ;
45
- OperationIdToBlockMap = operationIdToBlockMap ;
48
+ _exportQueue = exportQueue ;
49
+ _operationIdToBlockMap = operationIdToBlockMap ;
46
50
_clusterCacheFetch = clusterCacheFetch ;
47
51
}
48
52
49
- public PriorityQueue < RowItem , KustoPriority > ExportQueue { get ; }
50
-
51
- public IDictionary < string , RowItem > OperationIdToBlockMap { get ; }
52
-
53
53
public async Task < ClusterCache > GetClusterCacheAsync ( CancellationToken ct )
54
54
{
55
55
var cacheTask = _cacheTask ;
@@ -68,13 +68,43 @@ public async Task<ClusterCache> GetClusterCacheAsync(CancellationToken ct)
68
68
69
69
return await _cacheTask ! ;
70
70
}
71
+
72
+ #region Export queue
73
+ public void EnqueueExport ( RowItem blockItem , KustoPriority kustoPriority )
74
+ {
75
+ lock ( _exportQueue )
76
+ {
77
+ _exportQueue . Enqueue ( blockItem , kustoPriority ) ;
78
+ }
79
+ }
80
+
81
+ public bool TryDequeueExport ( [ MaybeNullWhen ( false ) ] out RowItem blockItem )
82
+ {
83
+ lock ( _exportQueue )
84
+ {
85
+ return _exportQueue . TryDequeue ( out blockItem , out _ ) ;
86
+ }
87
+ }
88
+ #endregion
89
+
90
+ #region Operation ID map
91
+ public int GetOperationIDCount ( )
92
+ {
93
+ lock ( _operationIdToBlockMap )
94
+ {
95
+ return _operationIdToBlockMap . Count ;
96
+ }
97
+ }
98
+ #endregion
71
99
}
72
100
#endregion
73
101
74
102
private static readonly TimeSpan CACHE_REFRESH_RATE = TimeSpan . FromMinutes ( 10 ) ;
75
103
76
104
private readonly IDictionary < Uri , ClusterQueue > _clusterToExportQueue =
77
105
new Dictionary < Uri , ClusterQueue > ( ) ;
106
+ private volatile int _isExportingTaskRunning = Convert . ToInt32 ( false ) ;
107
+ private volatile int _isExportedTaskRunning = Convert . ToInt32 ( false ) ;
78
108
79
109
public SourceTableExportingOrchestration (
80
110
RowItemGateway rowItemGateway ,
@@ -126,9 +156,9 @@ protected override void OnProcessRowItemAppended(RowItemAppend e, CancellationTo
126
156
private async Task OnQueueExportingItemAsync ( RowItem blockItem , CancellationToken ct )
127
157
{
128
158
var tableIdentity = blockItem . GetSourceTableIdentity ( ) ;
129
- var clusterQueue = GetClusterQueue ( blockItem , ct ) ;
159
+ var clusterQueue = EnsureClusterQueue ( blockItem , ct ) ;
130
160
131
- clusterQueue . ExportQueue . Enqueue (
161
+ clusterQueue . EnqueueExport (
132
162
blockItem ,
133
163
new KustoPriority (
134
164
tableIdentity . DatabaseName ,
@@ -137,6 +167,7 @@ private async Task OnQueueExportingItemAsync(RowItem blockItem, CancellationToke
137
167
tableIdentity . TableName ,
138
168
blockItem . BlockId ) ) ) ;
139
169
170
+ // We test since it might be coming from a persisted state which is "exporting" already
140
171
if ( blockItem . ParseState < SourceBlockState > ( ) == SourceBlockState . Planned )
141
172
{
142
173
var newBlockItem = blockItem . Clone ( ) ;
@@ -145,10 +176,60 @@ private async Task OnQueueExportingItemAsync(RowItem blockItem, CancellationToke
145
176
146
177
await RowItemGateway . AppendAsync ( newBlockItem , ct ) ;
147
178
}
179
+ if ( Interlocked . CompareExchange (
180
+ ref _isExportingTaskRunning ,
181
+ Convert . ToInt32 ( true ) ,
182
+ Convert . ToInt32 ( false ) ) == Convert . ToInt32 ( false ) )
183
+ {
184
+ BackgroundTaskContainer . AddTask ( OnExportingAsync ( ct ) ) ;
185
+ }
186
+ }
187
+
188
+ private async Task OnExportingAsync ( CancellationToken ct )
189
+ {
190
+ while ( true )
191
+ {
192
+ foreach ( var clusterQueue in GetClusterQueues ( ) )
193
+ {
194
+ var clusterCache = await clusterQueue . GetClusterCacheAsync ( ct ) ;
195
+
196
+ if ( clusterQueue . GetOperationIDCount ( ) > clusterCache . ExportCapacity
197
+ && clusterQueue . TryDequeueExport ( out var blockItem ) )
198
+ {
199
+ var tableIdentity = blockItem . GetSourceTableIdentity ( ) ;
200
+ var commandClient = DbClientFactory . GetDbCommandClient (
201
+ tableIdentity . ClusterUri ,
202
+ tableIdentity . DatabaseName ) ;
203
+ var operationId = await commandClient . ExportBlockAsync (
204
+ clusterCache . ExportRootUris ,
205
+ tableIdentity . TableName ,
206
+ blockItem . CursorStart ,
207
+ blockItem . CursorEnd ,
208
+ blockItem . IngestionTimeStart ,
209
+ blockItem . IngestionTimeEnd ,
210
+ ct ) ;
211
+ var newBlockItem = blockItem . Clone ( ) ;
212
+
213
+ newBlockItem . State = SourceBlockState . Exporting . ToString ( ) ;
214
+ newBlockItem . OperationId = operationId ;
215
+ await RowItemGateway . AppendAsync ( newBlockItem , ct ) ;
216
+ }
217
+ }
218
+ }
148
219
}
149
220
150
221
#region Cluster queue
151
- private ClusterQueue GetClusterQueue ( RowItem blockItem , CancellationToken ct )
222
+ private IImmutableList < ClusterQueue > GetClusterQueues ( )
223
+ {
224
+ lock ( _clusterToExportQueue )
225
+ {
226
+ return _clusterToExportQueue
227
+ . Values
228
+ . ToImmutableArray ( ) ;
229
+ }
230
+ }
231
+
232
+ private ClusterQueue EnsureClusterQueue ( RowItem blockItem , CancellationToken ct )
152
233
{
153
234
var tableIdentity = blockItem . GetSourceTableIdentity ( ) ;
154
235
0 commit comments