private static async Task MainAsync() { string poolId = "JobPrepReleaseSamplePool"; string jobId = "JobPrepReleaseSampleJob"; var settings = Config.LoadAccountSettings(); // Location of the file that the job tasks will work with, a text file in the // node's "shared" directory. string taskOutputFile = "$AZ_BATCH_NODE_SHARED_DIR/job_prep_and_release.txt"; // The job prep task will write the node ID to the text file in the shared directory string jobPrepCmdLine = $@"/bin/bash -c ""echo $AZ_BATCH_NODE_ID tasks: > {taskOutputFile}"""; // Each task then echoes its ID to the same text file string taskCmdLine = $@"/bin/bash -c ""echo $AZ_BATCH_TASK_ID >> {taskOutputFile}"""; // The job release task will then delete the text file from the shared directory string jobReleaseCmdLine = $@"/bin/bash -c ""rm {taskOutputFile}"""; BatchSharedKeyCredentials cred = new BatchSharedKeyCredentials(settings.BatchServiceUrl, settings.BatchAccountName, settings.BatchAccountKey); using (BatchClient batchClient = BatchClient.Open(cred)) { var pool = await BatchUtils.CreatePoolIfNotExistAsync(batchClient, poolId); var prepTask = new JobPreparationTask { CommandLine = jobPrepCmdLine }; var releaseTask = new JobReleaseTask { CommandLine = jobReleaseCmdLine }; var job = await BatchUtils.CreateJobIfNotExistAsync(batchClient, pool.Id, jobId, prepTask : prepTask, releaseTask : releaseTask); // Create the tasks that the job will execute List <CloudTask> tasks = new List <CloudTask>(); for (int i = 1; i <= 8; i++) { string taskId = "task" + i.ToString().PadLeft(3, '0'); string taskCommandLine = taskCmdLine; CloudTask task = new CloudTask(taskId, taskCommandLine); tasks.Add(task); } // Add the tasks in one API call as opposed to a separate AddTask call for each. Bulk task // submission helps to ensure efficient underlying API calls to the Batch service. Console.WriteLine("Submitting tasks and awaiting completion..."); await batchClient.JobOperations.AddTaskAsync(job.Id, tasks); // Wait for the tasks to complete before proceeding. The long timeout here is to allow time // for the nodes within the pool to be created and started if the pool had not yet been created. await batchClient.Utilities.CreateTaskStateMonitor().WhenAll( job.ListTasks(), TaskState.Completed, TimeSpan.FromMinutes(30)); Console.WriteLine("All tasks completed."); Console.WriteLine(); // Print the contents of the shared text file modified by the job preparation and other tasks. ODATADetailLevel nodeDetail = new ODATADetailLevel(selectClause: "id, state"); IPagedEnumerable <ComputeNode> nodes = batchClient.PoolOperations.ListComputeNodes(poolId, nodeDetail); await nodes.ForEachAsync(async (node) => { // Check to ensure that the node is Idle before attempting to pull the text file. // If the pool was just created, there is a chance that another node completed all // of the tasks prior to the other node(s) completing their startup procedure. if (node.State == ComputeNodeState.Idle) { var files = await node.ListNodeFiles().ToListAsync(); NodeFile sharedTextFile = await node.GetNodeFileAsync("shared/job_prep_and_release.txt"); Console.WriteLine("Contents of {0} on {1}:", sharedTextFile.Path, node.Id); Console.WriteLine("-------------------------------------------"); Console.WriteLine(await sharedTextFile.ReadAsStringAsync()); } }); // Terminate the job to mark it as Completed; this will initiate the Job Release Task on any node // that executed job tasks. Note that the Job Release Task is also executed when a job is deleted, // thus you need not call Terminate if you typically delete your jobs upon task completion. await batchClient.JobOperations.TerminateJobAsync(job.Id); // Wait for the job to reach state "Completed." Note that this wait is not typically necessary in // production code, but is done here to enable the checking of the release tasks exit code below. await BatchUtils.WaitForJobToReachStateAsync(batchClient, job.Id, JobState.Completed, TimeSpan.FromMinutes(2)); // Print the exit codes of the prep and release tasks by obtaining their execution info List <JobPreparationAndReleaseTaskExecutionInformation> prepReleaseInfo = await batchClient.JobOperations.ListJobPreparationAndReleaseTaskStatus(job.Id).ToListAsync(); foreach (JobPreparationAndReleaseTaskExecutionInformation info in prepReleaseInfo) { Console.WriteLine(); Console.WriteLine("{0}: ", info.ComputeNodeId); // If no tasks were scheduled to run on the node, the JobPreparationTaskExecutionInformation will be null if (info.JobPreparationTaskExecutionInformation != null) { Console.WriteLine(" Prep task exit code: {0}", info.JobPreparationTaskExecutionInformation.ExitCode); } // If no tasks were scheduled to run on the node, the JobReleaseTaskExecutionInformation will be null if (info.JobReleaseTaskExecutionInformation != null) { Console.WriteLine(" Release task exit code: {0}", info.JobReleaseTaskExecutionInformation.ExitCode); } } // Clean up the resources we've created in the Batch account Console.WriteLine(); Console.WriteLine("Delete job? [yes] no"); string response = Console.ReadLine().ToLower(); if (response != "n" && response != "no") { // Note that deleting the job will execute the job release task if the job was not previously terminated await batchClient.JobOperations.DeleteJobAsync(job.Id); } Console.WriteLine("Delete pool? [yes] no"); response = Console.ReadLine(); if (response != "n" && response != "no") { await batchClient.PoolOperations.DeletePoolAsync(poolId); } } }
private static async Task MainAsync() { //const string nodeSize = "standard_a1_v2"; const string nodeSize = "standard_a4_v2"; const int nodeCount = 4; const int maxTasksPerNode = 4; //can be set to 4* the number of cores on each node const int taskCount = 32; // Ensure there are enough tasks to help avoid hitting some timeout conditions below int minimumTaskCount = nodeCount * maxTasksPerNode * 2; if (taskCount < minimumTaskCount) { Console.WriteLine("You must specify at least two tasks per node core for this sample ({0} tasks in this configuration).", minimumTaskCount); Console.WriteLine(); // Not enough tasks, exit the application return; } // In this sample, the tasks simply ping localhost on the compute nodes; adjust these // values to simulate variable task duration const int minPings = 30; const int maxPings = 60; const string poolId = "ParallelTasksPoolDemo"; const string jobId = "ParallelTasksJob"; var settings = Config.LoadAccountSettings(); BatchSharedKeyCredentials cred = new BatchSharedKeyCredentials(settings.BatchServiceUrl, settings.BatchAccountName, settings.BatchAccountKey); using (BatchClient batchClient = BatchClient.Open(cred)) { var pool = await BatchUtils.CreatePoolIfNotExistAsync( batchClient, poolId, nodeSize : nodeSize, maxTasksPerNode : maxTasksPerNode, schedulingPolicy : new TaskSchedulingPolicy(ComputeNodeFillType.Pack), lowPriorityNodes : nodeCount); CloudJob job = await BatchUtils.CreateJobIfNotExistAsync(batchClient, poolId, jobId); // The job's tasks ping localhost a random number of times between minPings and maxPings. // Adjust the minPings/maxPings values above to experiment with different task durations. Random rand = new Random(); List <CloudTask> tasks = new List <CloudTask>(); for (int i = 1; i <= taskCount; i++) { string taskId = "task" + i.ToString().PadLeft(3, '0'); string taskCommandLine = "ping -c " + rand.Next(minPings, maxPings + 1).ToString() + " localhost"; CloudTask task = new CloudTask(taskId, taskCommandLine); tasks.Add(task); } // Pause execution until the pool is steady and its compute nodes are ready to accept jobs. // NOTE: Such a pause is not necessary within your own code. Tasks can be added to a job at any point and will be // scheduled to execute on a compute node as soon any node has reached Idle state. Because the focus of this sample // is the demonstration of running tasks in parallel on multiple compute nodes, we wait for all compute nodes to // complete initialization and reach the Idle state in order to maximize the number of compute nodes available for // parallelization. await BatchUtils.WaitForPoolToReachStateAsync(batchClient, poolId, AllocationState.Steady, TimeSpan.FromMinutes(30)); await BatchUtils.WaitForNodesToReachStateAsync(batchClient, poolId, ComputeNodeState.Idle, TimeSpan.FromMinutes(30)); // Add the tasks in one API call as opposed to a separate AddTask call for each. Bulk task // submission helps to ensure efficient underlying API calls to the Batch service. Console.WriteLine("Submitting tasks and awaiting completion..."); await batchClient.JobOperations.AddTaskAsync(job.Id, tasks); // Pause again to wait until *all* nodes are running tasks await BatchUtils.WaitForNodesToReachStateAsync(batchClient, poolId, ComputeNodeState.Running, TimeSpan.FromMinutes(2)); Stopwatch stopwatch = Stopwatch.StartNew(); // Print out task assignment information. Console.WriteLine(); await BatchUtils.PrintNodeTasksAsync(batchClient, poolId); Console.WriteLine(); // Pause execution while we wait for all of the tasks to complete Console.WriteLine("Waiting for task completion..."); Console.WriteLine(); try { await batchClient.Utilities.CreateTaskStateMonitor().WhenAll( job.ListTasks(), TaskState.Completed, TimeSpan.FromMinutes(30)); } catch (TimeoutException e) { Console.WriteLine(e.ToString()); } stopwatch.Stop(); // Obtain the tasks, specifying a detail level to limit the number of properties returned for each task. // If you have a large number of tasks, specifying a DetailLevel is extremely important in reducing the // amount of data transferred, lowering your query response times in increasing performance. ODATADetailLevel detail = new ODATADetailLevel(selectClause: "id,commandLine,nodeInfo,state"); IPagedEnumerable <CloudTask> allTasks = batchClient.JobOperations.ListTasks(job.Id, detail); // Get a collection of the completed tasks sorted by the compute nodes on which they executed List <CloudTask> completedTasks = allTasks .Where(t => t.State == TaskState.Completed) .OrderBy(t => t.ComputeNodeInformation.ComputeNodeId) .ToList(); // Print the completed task information Console.WriteLine(); Console.WriteLine("Completed tasks:"); string lastNodeId = string.Empty; foreach (CloudTask task in completedTasks) { if (!string.Equals(lastNodeId, task.ComputeNodeInformation.ComputeNodeId)) { Console.WriteLine(); Console.WriteLine(task.ComputeNodeInformation.ComputeNodeId); } lastNodeId = task.ComputeNodeInformation.ComputeNodeId; Console.WriteLine("\t{0}: {1}", task.Id, task.CommandLine); } // Get a collection of the uncompleted tasks which may exist if the TaskMonitor timeout was hit List <CloudTask> uncompletedTasks = allTasks .Where(t => t.State != TaskState.Completed) .OrderBy(t => t.Id) .ToList(); // Print a list of uncompleted tasks, if any Console.WriteLine(); Console.WriteLine("Uncompleted tasks:"); Console.WriteLine(); if (uncompletedTasks.Any()) { foreach (CloudTask task in uncompletedTasks) { Console.WriteLine("\t{0}: {1}", task.Id, task.CommandLine); } } else { Console.WriteLine("\t<none>"); } // Print some summary information Console.WriteLine(); Console.WriteLine(" Nodes: " + nodeCount); Console.WriteLine(" Node size: " + nodeSize); Console.WriteLine("Max tasks per node: " + pool.MaxTasksPerComputeNode); Console.WriteLine(" Tasks: " + tasks.Count); Console.WriteLine(" Duration: " + stopwatch.Elapsed); Console.WriteLine(); Console.WriteLine("Done!"); Console.WriteLine(); // Clean up the resources we've created in the Batch account Console.WriteLine(); Console.WriteLine("Delete job? [yes] no"); string response = Console.ReadLine().ToLower(); if (response != "n" && response != "no") { // Note that deleting the job will execute the job release task if the job was not previously terminated await batchClient.JobOperations.DeleteJobAsync(job.Id); } Console.WriteLine("Delete pool? [yes] no"); response = Console.ReadLine(); if (response != "n" && response != "no") { await batchClient.PoolOperations.DeletePoolAsync(poolId); } } }
private static async Task MainAsync() { const string poolId = "FileHandlingPool"; const string jobId = "FileHandlingJobDemo"; var settings = Config.LoadAccountSettings(); SetupStorage(settings.StorageAccountName, settings.StorageAccountKey); BatchSharedKeyCredentials cred = new BatchSharedKeyCredentials(settings.BatchServiceUrl, settings.BatchAccountName, settings.BatchAccountKey); using (BatchClient batchClient = BatchClient.Open(cred)) { var pool = await BatchUtils.CreatePoolIfNotExistAsync(batchClient, poolId); var job = await BatchUtils.CreateJobIfNotExistAsync(batchClient, poolId, jobId); //set up auto storage file ResourceFile autoStorageFile = ResourceFile.FromAutoStorageContainer(AutoStorageContainerName, AutoStorageFileName); Console.WriteLine("\n[INFO] Autostorage resource File reference: "); Console.WriteLine("AutoStorageContainer: " + autoStorageFile.AutoStorageContainerName); Console.WriteLine("FilePath: " + autoStorageFile.FilePath); //upload file to external storage and add it as a resource file string storageConnectionString = $"DefaultEndpointsProtocol=https;AccountName={settings.StorageAccountName};AccountKey={settings.StorageAccountKey}"; CloudStorageAccount storageAccount = CloudStorageAccount.Parse(storageConnectionString); CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient(); CloudBlobContainer externalContainer = blobClient.GetContainerReference(ExternalStorageContainerName); await externalContainer.CreateIfNotExistsAsync(); var externalFile = await UploadFileToContainer(blobClient, ExternalStorageContainerName, "resource_files/resource_file.txt", "resource_file.txt"); Console.WriteLine("\n[INFO] External storage resource File reference:"); Console.WriteLine("SAS Url: " + externalFile.HttpUrl); Console.WriteLine("FilePath: " + externalFile.FilePath); // using staging files API var filesToStage = new List <IFileStagingProvider>(); StagingStorageAccount fileStagingStorageAccount = new StagingStorageAccount( storageAccount: settings.StorageAccountName, storageAccountKey: settings.StorageAccountKey, blobEndpoint: storageAccount.BlobEndpoint.ToString()); FileToStage stagedFile = new FileToStage("resource_files/staged_file.txt", fileStagingStorageAccount); Console.WriteLine("\n[INFO] Staged File added:"); Console.WriteLine("Local File: " + stagedFile.LocalFileToStage); Console.WriteLine("Node File: " + stagedFile.NodeFileName); filesToStage.Add(stagedFile); // setup output files // Generate SAS for outputcontainer CloudBlobContainer outputContainer = blobClient.GetContainerReference(OutputContainerName); await outputContainer.CreateIfNotExistsAsync(); string containerSas = outputContainer.GetSharedAccessSignature(new SharedAccessBlobPolicy() { Permissions = SharedAccessBlobPermissions.Write, SharedAccessExpiryTime = DateTimeOffset.UtcNow.AddDays(1) }); string containerUrl = outputContainer.Uri.AbsoluteUri + containerSas; Console.WriteLine("\n[INFO] Output container: " + containerUrl); Console.WriteLine("\nPress return to continue..."); Console.ReadLine(); // Create tasks List <CloudTask> tasks = new List <CloudTask>(); for (var i = 1; i <= 10; i++) { var taskId = i.ToString().PadLeft(3, '0'); var commandLine = $@"/bin/bash -c ""echo 'Hello from {taskId}' && printf 'root dir:\n' > output.txt && ls -la >> output.txt && printf '\ninput dir:\n' >> output.txt && ls -la input >> output.txt"""; var task = new CloudTask(taskId, commandLine); // add resource files to task (one autostorage, one in external storage) task.ResourceFiles = new[] { autoStorageFile, externalFile }; // add staged files task.FilesToStage = filesToStage; // add output files var outputFiles = new List <OutputFile> { new OutputFile( filePattern: @"../std*.txt", destination: new OutputFileDestination(new OutputFileBlobContainerDestination( containerUrl: containerUrl, path: taskId)), uploadOptions: new OutputFileUploadOptions( uploadCondition: OutputFileUploadCondition.TaskCompletion)), new OutputFile( filePattern: @"output.txt", destination: new OutputFileDestination(new OutputFileBlobContainerDestination( containerUrl: containerUrl, path: taskId + @"\output.txt")), uploadOptions: new OutputFileUploadOptions( uploadCondition: OutputFileUploadCondition.TaskCompletion)), }; task.OutputFiles = outputFiles; tasks.Add(task); } Console.WriteLine("Submitting tasks and awaiting completion..."); // Add all tasks to the job. batchClient.JobOperations.AddTask(job.Id, tasks); await BatchUtils.WaitForTasksAndPrintOutputAsync(batchClient, job.ListTasks(), TimeSpan.FromMinutes(30)); // Clean up Batch resources (if the user so chooses) Console.WriteLine(); Console.Write("Delete job? [yes] no: "); string response = Console.ReadLine().ToLower(); if (response != "n" && response != "no") { batchClient.JobOperations.DeleteJob(jobId); } Console.Write("Delete pool? [yes] no: "); response = Console.ReadLine().ToLower(); if (response != "n" && response != "no") { batchClient.PoolOperations.DeletePool(poolId); } } }
private static async Task MainAsync() { const int nodeCount = 1; string poolId = "TaskDependenciesSamplePool"; string jobId = "TaskDependenciesJob"; var settings = Config.LoadAccountSettings(); BatchSharedKeyCredentials cred = new BatchSharedKeyCredentials(settings.BatchServiceUrl, settings.BatchAccountName, settings.BatchAccountKey); using (BatchClient batchClient = BatchClient.Open(cred)) { var pool = await BatchUtils.CreatePoolIfNotExistAsync(batchClient, poolId, lowPriorityNodes : nodeCount); var job = await BatchUtils.CreateJobIfNotExistAsync(batchClient, pool.Id, jobId, usesTaskDependencies : true); string taskOutputFile = "$AZ_BATCH_NODE_SHARED_DIR/task_output.txt"; // Create the collection of tasks that will be added to the job. List <CloudTask> tasks = new List <CloudTask> { // 'Rain' and 'Sun' don't depend on any other tasks new CloudTask("Rain", $"/bin/bash -c \"echo Rain >> {taskOutputFile}\""), new CloudTask("Sun", $"/bin/bash -c \"echo Sun >> {taskOutputFile}\""), // Task 'Flowers' depends on completion of both 'Rain' and 'Sun' // before it is run. new CloudTask("Flowers", $"/bin/bash -c \"echo Flowers >> {taskOutputFile}\"") { DependsOn = TaskDependencies.OnIds("Rain", "Sun") }, // Tasks 1, 2, and 3 don't depend on any other tasks. Because // we will be using them for a task range dependency, we must // specify string representations of integers as their ids. new CloudTask("1", $"/bin/bash -c \"echo 1 >> {taskOutputFile}\""), new CloudTask("2", $"/bin/bash -c \"echo 2 >> {taskOutputFile}\""), new CloudTask("3", $"/bin/bash -c \"echo 3 >> {taskOutputFile}\""), // Task dependency on ID range new CloudTask("Final", $"/bin/bash -c \"echo Final >> {taskOutputFile}\"") { DependsOn = TaskDependencies.OnIdRange(1, 3) }, // Task A is the parent task. new CloudTask("A", $"/bin/bash -c \"echo A >> {taskOutputFile}\"") { // Specify exit conditions for task A and their dependency actions. ExitConditions = new ExitConditions { // If task A exits with a pre-processing error, block any downstream tasks (in this example, task B). PreProcessingError = new ExitOptions { DependencyAction = DependencyAction.Block }, // If task A exits with the specified error codes, block any downstream tasks (in this example, task B). ExitCodes = new List <ExitCodeMapping> { new ExitCodeMapping(10, new ExitOptions() { DependencyAction = DependencyAction.Block }), new ExitCodeMapping(20, new ExitOptions() { DependencyAction = DependencyAction.Block }) }, // If task A succeeds or fails with any other error, any downstream tasks become eligible to run // (in this example, task B). Default = new ExitOptions { DependencyAction = DependencyAction.Satisfy }, } }, // Task B depends on task A. Whether it becomes eligible to run depends on how task A exits. new CloudTask("B", $"/bin/bash -c \"echo B >> {taskOutputFile}\"") { DependsOn = TaskDependencies.OnId("A") }, }; // Add the tasks in one API call as opposed to a separate AddTask call for each. Bulk task // submission helps to ensure efficient underlying API calls to the Batch service. Console.WriteLine("Submitting tasks and awaiting completion..."); await batchClient.JobOperations.AddTaskAsync(job.Id, tasks); // Wait for the tasks to complete before proceeding. The long timeout here is to allow time // for the nodes within the pool to be created and started if the pool had not yet been created. await batchClient.Utilities.CreateTaskStateMonitor().WhenAll( job.ListTasks(), TaskState.Completed, TimeSpan.FromMinutes(30)); Console.WriteLine("All tasks completed."); Console.WriteLine(); // Print the contents of the shared text file modified by the job preparation and other tasks. ODATADetailLevel nodeDetail = new ODATADetailLevel(selectClause: "id, state"); IPagedEnumerable <ComputeNode> nodes = batchClient.PoolOperations.ListComputeNodes(poolId, nodeDetail); await nodes.ForEachAsync(async (node) => { // Check to ensure that the node is Idle before attempting to pull the text file. // If the pool was just created, there is a chance that another node completed all // of the tasks prior to the other node(s) completing their startup procedure. if (node.State == ComputeNodeState.Idle) { NodeFile sharedTextFile = await node.GetNodeFileAsync("shared/task_output.txt"); Console.WriteLine("Contents of {0} on {1}:", sharedTextFile.Path, node.Id); Console.WriteLine("-------------------------------------------"); Console.WriteLine(await sharedTextFile.ReadAsStringAsync()); } }); // Clean up the resources we've created in the Batch account Console.WriteLine(); Console.WriteLine("Delete job? [yes] no"); string response = Console.ReadLine().ToLower(); if (response != "n" && response != "no") { // Note that deleting the job will execute the job release task if the job was not previously terminated await batchClient.JobOperations.DeleteJobAsync(job.Id); } Console.WriteLine("Delete pool? [yes] no"); response = Console.ReadLine(); if (response != "n" && response != "no") { await batchClient.PoolOperations.DeletePoolAsync(poolId); } } }