Skip to content

Commit 4f74eb8

Browse files
authored
[DataMovement] Pause and Resume (Azure#35160)
* WIP - make APIs public * WIP - Adding conversions header to stream * WIP - added back api to link to job plan * WIP * WIP * WIP - changed from marshal type to unsafe to get struct size in mmf * WIP * WIP * WIP - convert JobPartPlanHeader to manage bytes manually * WIP * WIP - Working on deserializing * WIP - serialize and deserialize tests * WIP - fixed missing values deserialize and serialize methods * WIP - move test infra; more checkpointer tests and fixes * WIP - correct transfer manager to checkpointer changes * Added Pause Resume Tests WIP: pause then resume tests, directory pause, sync tests * ExportAPI * Undo unrelated changes * WIP - fix null object error and adding in transfer to track failures to end of test * Deleted unused models; PR Comment regarding API exposure of checkpointer; small test fix * Addressed PR comments as of 4/4/2023, waiting on replies * Export API * WIP * WIP - implemented wait on complete pause * PR comments; fix to pause resume then pause; WIP - completedwithfailures tests * Fixed directory failed test issue * Record tests * Remove use of mapnames as it's unsupported in linux and mac; fix tests slash delimiter failure * more path combine change for mac/linux * Add check for CheckAndUpdateCancellationStatus on completed and skipped events * Revert last commit * Ignore flakey tests
1 parent 0db2086 commit 4f74eb8

File tree

78 files changed

+6053
-1380
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

78 files changed

+6053
-1380
lines changed

sdk/storage/Azure.Storage.DataMovement.Blobs/src/Azure.Storage.DataMovement.Blobs.csproj

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,8 +99,6 @@
9999
<Compile Include="$(AzureStorageDataMovementSharedSources)ProgressMultipleParititonedStream.cs" LinkBase="Shared\DataMovement" />
100100
<Compile Include="$(AzureStorageDataMovementSharedSources)PathScanner.cs" LinkBase="Shared\DataMovement" />
101101
<Compile Include="$(AzureStorageDataMovementSharedSources)PathScannerFactory.cs" LinkBase="Shared\DataMovement" />
102-
<Compile Include="$(AzureStorageDataMovementSharedSources)PlanJobWriter.cs" LinkBase="Shared\DataMovement" />
103-
<Compile Include="$(AzureStorageDataMovementSharedSources)PlanJobWriterFactory.cs" LinkBase="Shared\DataMovement" />
104102
<Compile Include="$(AzureStorageDataMovementSharedSources)StorageManagerTransferStatus.cs" LinkBase="Shared\DataMovement" />
105103
</ItemGroup>
106104
<ItemGroup>

sdk/storage/Azure.Storage.DataMovement.Blobs/tests/Azure.Storage.DataMovement.Blobs.Tests.csproj

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,18 @@
77
</PropertyGroup>
88
<ItemGroup>
99
<ProjectReference Include="$(MSBuildThisFileDirectory)..\src\Azure.Storage.DataMovement.Blobs.csproj" />
10+
<ProjectReference Include="$(MSBuildThisFileDirectory)..\..\Azure.Storage.DataMovement\src\Azure.Storage.DataMovement.csproj" />
1011
</ItemGroup>
1112
<ItemGroup>
1213
<Compile Include="$(AzureStorageDataMovementTestSharedSources)DataMovementTestBase.cs" LinkBase="Shared\DataMovement" />
14+
<Compile Include="$(AzureStorageDataMovementTestSharedSources)DisposingLocalDirectory.cs" LinkBase="Shared\DataMovement" />
1315
</ItemGroup>
1416
<ItemGroup>
15-
<Compile Include="$(AzureStorageSharedTestSources)\**\*.cs" LinkBase="Shared" />
17+
<Compile Include="$(AzureStorageDataMovementSharedSources)..\JobPlanModels\*.cs" LinkBase="Shared\DataMovement\JobPlanModels" />
18+
<Compile Include="$(AzureStorageDataMovementSharedSources)DataMovementExtensions.cs" LinkBase="Shared\DataMovement" />
19+
</ItemGroup>
20+
<ItemGroup>
21+
<Compile Include="$(AzureStorageSharedTestSources)\**\*.cs" LinkBase="Shared\Storage" />
1622
<Compile Remove="$(AzureStorageSharedTestSources)\AzuriteFixture.cs" />
1723
<Compile Remove="$(AzureStorageSharedTestSources)\AzuriteNUnitFixture.cs" />
1824
<Compile Include="$(MSBuildThisFileDirectory)..\..\Azure.Storage.Blobs\tests\BlobsClientTestFixtureAttribute.cs" LinkBase="Shared" />

sdk/storage/Azure.Storage.DataMovement/api/Azure.Storage.DataMovement.netstandard2.0.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ internal DataTransfer() { }
88
public Azure.Storage.DataMovement.StorageTransferStatus TransferStatus { get { throw null; } }
99
public System.Threading.Tasks.Task AwaitCompletion(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
1010
public void EnsureCompleted(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { }
11+
public System.Threading.Tasks.Task<bool> TryPauseAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
1112
}
1213
[System.FlagsAttribute]
1314
public enum ErrorHandlingOptions
@@ -106,6 +107,8 @@ public enum StorageTransferStatus
106107
Completed = 4,
107108
CompletedWithSkippedTransfers = 5,
108109
CompletedWithFailedTransfers = 6,
110+
PauseInProgress = 7,
111+
CancellationInProgress = 8,
109112
}
110113
public partial class TransferManager : System.IAsyncDisposable
111114
{
@@ -114,10 +117,13 @@ public TransferManager(Azure.Storage.DataMovement.TransferManagerOptions options
114117
public virtual System.Threading.Tasks.Task<Azure.Storage.DataMovement.DataTransfer> StartTransferAsync(Azure.Storage.DataMovement.StorageResource sourceResource, Azure.Storage.DataMovement.StorageResource destinationResource, Azure.Storage.DataMovement.Models.SingleTransferOptions transferOptions = null) { throw null; }
115118
public virtual System.Threading.Tasks.Task<Azure.Storage.DataMovement.DataTransfer> StartTransferAsync(Azure.Storage.DataMovement.StorageResourceContainer sourceResource, Azure.Storage.DataMovement.StorageResourceContainer destinationResource, Azure.Storage.DataMovement.Models.ContainerTransferOptions transferOptions = null) { throw null; }
116119
System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync() { throw null; }
120+
public virtual System.Threading.Tasks.Task<bool> TryPauseTransferAsync(Azure.Storage.DataMovement.DataTransfer transfer, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
121+
public virtual System.Threading.Tasks.Task<bool> TryPauseTransferAsync(string transferId, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
117122
}
118123
public partial class TransferManagerOptions
119124
{
120125
public TransferManagerOptions() { }
126+
public Azure.Storage.DataMovement.Models.TransferCheckpointerOptions CheckpointerOptions { get { throw null; } set { } }
121127
public Azure.Storage.DataMovement.ErrorHandlingOptions ErrorHandling { get { throw null; } set { } }
122128
public int? MaximumConcurrency { get { throw null; } set { } }
123129
}
@@ -135,6 +141,7 @@ public ContainerTransferOptions() { }
135141
public Azure.Storage.DataMovement.StorageResourceCreateMode CreateMode { get { throw null; } set { } }
136142
public long? InitialTransferSize { get { throw null; } set { } }
137143
public long? MaximumTransferChunkSize { get { throw null; } set { } }
144+
public string ResumeFromCheckpointId { get { throw null; } set { } }
138145
public event Azure.Core.SyncAsyncEventHandler<Azure.Storage.DataMovement.Models.SingleTransferCompletedEventArgs> SingleTransferCompleted { add { } remove { } }
139146
public event Azure.Core.SyncAsyncEventHandler<Azure.Storage.DataMovement.Models.TransferFailedEventArgs> TransferFailed { add { } remove { } }
140147
public event Azure.Core.SyncAsyncEventHandler<Azure.Storage.DataMovement.Models.TransferSkippedEventArgs> TransferSkipped { add { } remove { } }
@@ -174,6 +181,7 @@ public SingleTransferOptions() { }
174181
public Azure.Storage.DataMovement.StorageResourceCreateMode CreateMode { get { throw null; } set { } }
175182
public long? InitialTransferSize { get { throw null; } set { } }
176183
public long? MaximumTransferChunkSize { get { throw null; } set { } }
184+
public string ResumeFromCheckpointId { get { throw null; } set { } }
177185
public event Azure.Core.SyncAsyncEventHandler<Azure.Storage.DataMovement.Models.TransferFailedEventArgs> TransferFailed { add { } remove { } }
178186
public event Azure.Core.SyncAsyncEventHandler<Azure.Storage.DataMovement.Models.TransferSkippedEventArgs> TransferSkipped { add { } remove { } }
179187
public event Azure.Core.SyncAsyncEventHandler<Azure.Storage.DataMovement.Models.TransferStatusEventArgs> TransferStatus { add { } remove { } }
@@ -206,6 +214,10 @@ public partial class StorageResourceWriteToOffsetOptions
206214
public StorageResourceWriteToOffsetOptions() { }
207215
public string BlockId { get { throw null; } }
208216
}
217+
public partial class TransferCheckpointerOptions
218+
{
219+
public TransferCheckpointerOptions(string localCheckpointerPath) { }
220+
}
209221
public enum TransferCopyMethod
210222
{
211223
None = 0,

sdk/storage/Azure.Storage.DataMovement/src/Azure.Storage.DataMovement.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<Project Sdk="Microsoft.NET.Sdk">
1+
<Project Sdk="Microsoft.NET.Sdk">
22
<PropertyGroup>
33
<RequiredTargetFrameworks>netstandard2.0</RequiredTargetFrameworks>
44
<TargetFrameworks>$(RequiredTargetFrameworks)</TargetFrameworks>

sdk/storage/Azure.Storage.DataMovement/src/JobPartInternal.cs

Lines changed: 112 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@
55
using System.Collections.Generic;
66
using System.Globalization;
77
using System.IO;
8-
using System.Linq;
98
using System.Threading;
109
using System.Threading.Tasks;
11-
using System.Xml.Linq;
1210
using Azure.Core;
1311
using Azure.Storage.DataMovement.Models;
12+
using Azure.Storage.DataMovement.JobPlanModels;
13+
using System.Linq;
1414

1515
namespace Azure.Storage.DataMovement
1616
{
@@ -36,7 +36,7 @@ internal abstract class JobPartInternal
3636
///
3737
/// Will be disposed of once all tasks of the job have completed or have been cancelled.
3838
/// </summary>
39-
internal CancellationTokenSource _cancellationTokenSource { get; set; }
39+
internal CancellationToken _cancellationToken { get; set; }
4040

4141
/// <summary>
4242
/// Plan file writer for the respective job.
@@ -119,6 +119,9 @@ internal abstract class JobPartInternal
119119
/// </summary>
120120
public SyncAsyncEventHandler<SingleTransferCompletedEventArgs> SingleTransferCompletedEventHandler { get; internal set; }
121121

122+
private List<Task<bool>> _chunkTasks;
123+
private List<TaskCompletionSource<bool>> _chunkTaskSources;
124+
122125
/// <summary>
123126
/// Array pools for reading from streams to upload
124127
/// </summary>
@@ -143,18 +146,19 @@ internal JobPartInternal(
143146
SyncAsyncEventHandler<TransferFailedEventArgs> failedEventHandler,
144147
SyncAsyncEventHandler<TransferSkippedEventArgs> skippedEventHandler,
145148
SyncAsyncEventHandler<SingleTransferCompletedEventArgs> singleTransferEventHandler,
146-
CancellationTokenSource cancellationTokenSource,
149+
CancellationToken cancellationToken,
150+
StorageTransferStatus jobPartStatus = StorageTransferStatus.Queued,
147151
long? length = default)
148152
{
149-
JobPartStatus = StorageTransferStatus.Queued;
153+
JobPartStatus = jobPartStatus;
150154
PartNumber = partNumber;
151155
_dataTransfer = dataTransfer;
152156
_sourceResource = sourceResource;
153157
_destinationResource = destinationResource;
154158
_errorHandling = errorHandling;
155159
_createMode = createMode;
156160
_checkpointer = checkpointer;
157-
_cancellationTokenSource = cancellationTokenSource;
161+
_cancellationToken = cancellationToken;
158162
_arrayPool = arrayPool;
159163
PartTransferStatusEventHandler = jobPartEventHandler;
160164
TransferStatusEventHandler = statusEventHandler;
@@ -178,26 +182,75 @@ internal JobPartInternal(
178182
}
179183

180184
Length = length;
185+
_chunkTasks = new List<Task<bool>>();
186+
_chunkTaskSources = new List<TaskCompletionSource<bool>>();
181187
}
182188

183189
public void SetQueueChunkDelegate(QueueChunkDelegate chunkDelegate)
184190
{
185191
QueueChunk = chunkDelegate;
186192
}
187193

194+
/// <summary>
195+
/// Queues the task to the main chunk channel and also appends the tracking
196+
/// completion source to the task. So we know the state of each chunk especially
197+
/// when we're looking to stop/pause the job part.
198+
/// </summary>
199+
/// <returns></returns>
200+
public async Task QueueChunkToChannelAsync(Task chunkTask)
201+
{
202+
// Attach TaskCompletionSource
203+
TaskCompletionSource<bool> chunkCompleted = new TaskCompletionSource<bool>(
204+
false,
205+
TaskCreationOptions.RunContinuationsAsynchronously);
206+
_chunkTaskSources.Add(chunkCompleted);
207+
_chunkTasks.Add(chunkCompleted.Task);
208+
209+
await QueueChunk(
210+
async () =>
211+
{
212+
await chunkTask.ConfigureAwait(false);
213+
chunkCompleted.SetResult(true);
214+
await CheckAndUpdateCancellationStatusAsync().ConfigureAwait(false);
215+
}).ConfigureAwait(false);
216+
}
217+
188218
/// <summary>
189219
/// Processes the job to job parts
190220
/// </summary>
191221
/// <returns>An IEnumerable that contains the job chunks</returns>
192222
public abstract Task ProcessPartToChunkAsync();
193223

194-
internal async Task TriggerCancellation(StorageTransferStatus status)
224+
/// <summary>
225+
/// Triggers the cancellation for the Job Part.
226+
///
227+
/// If the status is set to <see cref="StorageTransferStatus.Paused"/>
228+
/// and any chunks is still processing to be cancelled is will be set to <see cref="StorageTransferStatus.PauseInProgress"/>
229+
/// until the chunks finish then it will be set to <see cref="StorageTransferStatus.Paused"/>.
230+
///
231+
/// If the status is set to <see cref="StorageTransferStatus.CompletedWithFailedTransfers"/>
232+
/// and any chunks is still processing to be cancelled is will be set to <see cref="StorageTransferStatus.CancellationInProgress"/>
233+
/// until the chunks finish then it will be set to <see cref="StorageTransferStatus.CompletedWithFailedTransfers"/>.
234+
/// </summary>
235+
/// <returns></returns>
236+
internal async Task TriggerCancellationAsync()
195237
{
196-
if (!_cancellationTokenSource.IsCancellationRequested)
238+
if (!_cancellationToken.IsCancellationRequested)
197239
{
198-
_cancellationTokenSource.Cancel();
240+
_dataTransfer._state.TriggerCancellation();
241+
}
242+
// Set the status to Pause/CancellationInProgress
243+
if (StorageTransferStatus.PauseInProgress == _dataTransfer.TransferStatus)
244+
{
245+
// It's possible that the status hasn't propagated down to the job part
246+
// status yet here since we pause from the data transfer object.
247+
await OnTransferStatusChanged(StorageTransferStatus.PauseInProgress).ConfigureAwait(false);
248+
}
249+
else
250+
{
251+
// It's a cancellation if a pause wasn't called.
252+
await OnTransferStatusChanged(StorageTransferStatus.CancellationInProgress).ConfigureAwait(false);
199253
}
200-
await OnTransferStatusChanged(status).ConfigureAwait(false);
201254
_dataTransfer._state.ResetTransferredBytes();
202255
}
203256

@@ -223,12 +276,15 @@ internal async Task OnTransferStatusChanged(StorageTransferStatus transferStatus
223276
{
224277
await InvokeSingleCompletedArg().ConfigureAwait(false);
225278
}
279+
// Set the status in the checkpointer
280+
await SetCheckpointerStatus(transferStatus).ConfigureAwait(false);
281+
226282
// TODO: change to RaiseAsync
227283
await PartTransferStatusEventHandler.Invoke(new TransferStatusEventArgs(
228284
_dataTransfer.Id,
229285
transferStatus,
230286
false,
231-
_cancellationTokenSource.Token)).ConfigureAwait(false);
287+
_cancellationToken)).ConfigureAwait(false);
232288
}
233289
}
234290

@@ -251,12 +307,12 @@ await SingleTransferCompletedEventHandler.Invoke(
251307
_sourceResource,
252308
_destinationResource,
253309
false,
254-
_cancellationTokenSource.Token)).ConfigureAwait(false);
310+
_cancellationToken)).ConfigureAwait(false);
255311
}
256312
}
257313

258314
/// <summary>
259-
/// Invokes Failed Argument
315+
/// Invokes Skipped Argument Event.
260316
/// </summary>
261317
public async virtual Task InvokeSkippedArg()
262318
{
@@ -268,13 +324,13 @@ await TransferSkippedEventHandler.Invoke(new TransferSkippedEventArgs(
268324
_sourceResource,
269325
_destinationResource,
270326
false,
271-
_cancellationTokenSource.Token)).ConfigureAwait(false);
327+
_cancellationToken)).ConfigureAwait(false);
272328
}
273329
await OnTransferStatusChanged(StorageTransferStatus.CompletedWithSkippedTransfers).ConfigureAwait(false);
274330
}
275331

276332
/// <summary>
277-
/// Invokes Failed Argument
333+
/// Invokes Failed Argument Event.
278334
/// </summary>
279335
public async virtual Task InvokeFailedArg(Exception ex)
280336
{
@@ -287,10 +343,34 @@ await TransferFailedEventHandler.Invoke(new TransferFailedEventArgs(
287343
_destinationResource,
288344
ex,
289345
false,
290-
_cancellationTokenSource.Token)).ConfigureAwait(false);
346+
_cancellationToken)).ConfigureAwait(false);
291347
}
292348
// Trigger job cancellation if the failed handler is enabled
293-
await TriggerCancellation(StorageTransferStatus.CompletedWithFailedTransfers).ConfigureAwait(false);
349+
await TriggerCancellationAsync().ConfigureAwait(false);
350+
await CheckAndUpdateCancellationStatusAsync().ConfigureAwait(false);
351+
}
352+
353+
public async virtual Task AddJobPartToCheckpointer(int chunksTotal)
354+
{
355+
JobPartPlanHeader header = this.ToJobPartPlanHeader(StorageTransferStatus.InProgress);
356+
using (Stream stream = new MemoryStream())
357+
{
358+
header.Serialize(stream);
359+
await _checkpointer.AddNewJobPartAsync(
360+
transferId: _dataTransfer.Id,
361+
partNumber: PartNumber,
362+
chunksTotal: chunksTotal,
363+
headerStream: stream,
364+
cancellationToken: _cancellationToken).ConfigureAwait(false);
365+
}
366+
}
367+
368+
internal async virtual Task SetCheckpointerStatus(StorageTransferStatus status)
369+
{
370+
await _checkpointer.SetJobPartTransferStatusAsync(
371+
transferId: _dataTransfer.Id,
372+
partNumber: PartNumber,
373+
status: status).ConfigureAwait(false);
294374
}
295375

296376
internal long CalculateBlockSize(long length)
@@ -381,5 +461,20 @@ internal static long ParseRangeTotalLength(string range)
381461
absolutePosition += blockLength;
382462
}
383463
}
464+
465+
internal async Task CheckAndUpdateCancellationStatusAsync()
466+
{
467+
if (_chunkTasks.All((Task task) => (task.IsCompleted)))
468+
{
469+
if (_dataTransfer.TransferStatus == StorageTransferStatus.PauseInProgress)
470+
{
471+
await OnTransferStatusChanged(StorageTransferStatus.Paused).ConfigureAwait(false);
472+
}
473+
else if (_dataTransfer.TransferStatus == StorageTransferStatus.CancellationInProgress)
474+
{
475+
await OnTransferStatusChanged(StorageTransferStatus.CompletedWithFailedTransfers).ConfigureAwait(false);
476+
}
477+
}
478+
}
384479
}
385480
}

0 commit comments

Comments
 (0)