コード例 #1
0
ファイル: MessageProcessor.cs プロジェクト: CedarLogic/Dash
        static bool ProcessMessage(QueueMessage message, int? invisibilityTimeout = null)
        {
            var messageProcessed = false;
            Guid contextId = message.CorrelationId.HasValue ? message.CorrelationId.Value : Guid.Empty;
            using (var ctx = new CorrelationContext(contextId))
            {
                switch (message.MessageType)
                {
                    case MessageTypes.BeginReplicate:
                        messageProcessed = DoReplicateJob(message, invisibilityTimeout);
                        break;

                    case MessageTypes.ReplicateProgress:
                        messageProcessed = DoReplicateProgressJob(message, invisibilityTimeout);
                        break;

                    case MessageTypes.DeleteReplica:
                        messageProcessed = DoDeleteReplicaJob(message, invisibilityTimeout);
                        break;

                    case MessageTypes.Unknown:
                    default:
                        DashTrace.TraceWarning("Unable to process unknown message type from async queue [{0}]. Payload: {1}",
                            message.MessageType,
                            message.ToString());
                        // Let this message bounce around for a bit - there may be a different version running
                        // on another instance that knows about this message. It will be automatically discarded
                        // after exceeding the deque limit.
                        messageProcessed = false;
                        break;
                }
            }
            return messageProcessed;
        }
コード例 #2
0
ファイル: MessageProcessor.cs プロジェクト: CedarLogic/Dash
 static bool DoDeleteReplicaJob(QueueMessage message, int? invisibilityTimeout = null)
 {
     return BlobReplicator.DeleteReplica(
         message.Payload[DeleteReplicaPayload.Source],
         message.Payload[DeleteReplicaPayload.Container],
         message.Payload[DeleteReplicaPayload.BlobName],
         message.Payload[DeleteReplicaPayload.ETag]);
 }
コード例 #3
0
ファイル: MessageProcessor.cs プロジェクト: CedarLogic/Dash
 static bool DoReplicateJob(QueueMessage message, int? invisibilityTimeout = null)
 {
     return BlobReplicator.BeginBlobReplication(
         message.Payload[ReplicatePayload.Source],
         message.Payload[ReplicatePayload.Destination],
         message.Payload[ReplicatePayload.Container],
         message.Payload[ReplicatePayload.BlobName],
         invisibilityTimeout);
 }
コード例 #4
0
ファイル: WorkerQueueTests.cs プロジェクト: farukc/Dash
        public void BasicQueueTest()
        {
            Dictionary<string, string> payload = new Dictionary<string, string>();
            payload.Add(ReplicatePayload.Source, "foo");
            payload.Add(ReplicatePayload.Destination, "bar");
            QueueMessage message = new QueueMessage(MessageTypes.BeginReplicate, payload);
            _queue.Enqueue(message);

            QueueMessage fromServer = _queue.Dequeue();
            Assert.AreEqual(fromServer.MessageType, MessageTypes.BeginReplicate);
            var servPayload = fromServer.Payload;
            Assert.AreEqual(servPayload[ReplicatePayload.Source], payload[ReplicatePayload.Source]);
            Assert.AreEqual(servPayload[ReplicatePayload.Destination], payload[ReplicatePayload.Destination]);
            fromServer.Delete();
        }
コード例 #5
0
        public async Task<IHttpActionResult> UpdateConfiguration(Configuration newConfig)
        {
            return await DoActionAsync("ConfigurationController.UpdateConfiguration", async (serviceClient) =>
            {
                UpdateConfigStatus.ConfigUpdate operationStatus = null;
                try
                {
                    // Do some validation checks first
                    var operationStatusTask = UpdateConfigStatus.GetActiveStatus();
                    var serviceConfigTask = serviceClient.GetDeploymentConfiguration();
                    await Task.WhenAll(operationStatusTask, serviceConfigTask);
                    if (operationStatusTask.Result != null)
                    {
                        return BadRequest(String.Format("Operation: {0} is already underway. Wait for this operation to complete before attempting further updates.", operationStatusTask.Result.OperationId));
                    }
                    var serviceSettings = AzureServiceConfiguration.GetSettingsProjected(serviceConfigTask.Result);
                    if (!CompareAccountName(serviceSettings, newConfig.AccountSettings, DashConfiguration.KeyNamespaceAccount))
                    {
                        return BadRequest("Cannot change the namespace account name once is has been set.");
                    }
                    int accountIndex = 0;
                    foreach (var currentAccount in serviceSettings
                                                        .Where(AzureServiceConfiguration.SettingPredicateScaleoutStorage))
                    {
                        if (!CompareAccountName(currentAccount, newConfig.ScaleAccounts.Accounts.ElementAtOrDefault(accountIndex++)))
                        {
                            var removedAccount = ParseConnectionString(currentAccount.Item2);
                            return BadRequest(String.Format("Data account [{0]} cannot be removed.", removedAccount.AccountName));
                        }
                    }

                    // Do some preamble work here synchronously (begin creation of any new storage accounts - we need to return the keys),
                    // but then enqueue a message on our async worker queue to complete the full update in a reliable manner without having
                    // the client wait forever for a response. The response will be an operationid which can be checked on by calling
                    // the /operations/operationid endpoint.
                    var operationId = DashTrace.CorrelationId.ToString();
                    // Reconcile storage accounts - any new accounts (indicated by a blank key) we will create immediately & include the key in the returned config
                    var newAccounts = new[] { 
                            ParseConnectionString(newConfig.AccountSettings, DashConfiguration.KeyNamespaceAccount, true),
                            ParseConnectionString(newConfig.AccountSettings, DashConfiguration.KeyDiagnosticsAccount, true)
                        }
                        .Concat(newConfig.ScaleAccounts.Accounts
                            .Select(account => new StorageAccountCreationInfo
                            {
                                AccountInfo = account,
                            }))
                        .Where(account => account != null && account.AccountInfo != null && String.IsNullOrWhiteSpace(account.AccountInfo.AccountKey))
                        .ToList();
                    // Start our operation log - use the namespace account specified in the configuration
                    var namespaceAccount = DashConfiguration.NamespaceAccount;
                    // See if they've specified an account in the new config
                    var toBeCreatedNamespace = newAccounts
                        .FirstOrDefault(account => String.Equals(account.ConfigKey, DashConfiguration.KeyNamespaceAccount, StringComparison.OrdinalIgnoreCase));
                    if (toBeCreatedNamespace == null)
                    {
                        CloudStorageAccount.TryParse(newConfig.AccountSettings[DashConfiguration.KeyNamespaceAccount], out namespaceAccount);
                    }
                    operationStatus = await UpdateConfigStatus.GetConfigUpdateStatus(operationId, namespaceAccount);
                    await operationStatus.UpdateStatus(UpdateConfigStatus.States.NotStarted, "Begin service update process. OperationId: [{0}]", operationId);
                    operationStatus.AccountsToBeCreated = newAccounts
                        .Select(account => account.AccountInfo.AccountName)
                        .ToList();
                    if (newAccounts.Any())
                    {
                        await operationStatus.UpdateStatus(UpdateConfigStatus.States.CreatingAccounts, "Creating new storage accounts: [{0}]", String.Join(", ", operationStatus.AccountsToBeCreated));
                        var newAccountTasks = await CreateStorageAccounts(serviceClient, newConfig, newAccounts);
                        await Task.WhenAll(newAccountTasks);
                        // If we just created a new account for the namespace, wire it up now
                        if (CloudStorageAccount.TryParse(newConfig.AccountSettings[DashConfiguration.KeyNamespaceAccount], out namespaceAccount))
                        {
                            operationStatus.StatusHandler.UpdateCloudStorageAccount(namespaceAccount);
                        }
                        // Switch AccountsToBeCreated to the request ids that the async task can verify are completed
                        operationStatus.AccountsToBeCreated = newAccountTasks
                            .Select(newAccoutTask => newAccoutTask.Result)
                            .Where(requestId => !String.IsNullOrWhiteSpace(requestId))
                            .ToList();
                        await operationStatus.UpdateStatus(UpdateConfigStatus.States.CreatingAccounts, "Creation of new storage accounts initiated.");
                    }
                    // TODO: When we enable storage analytics (or at least a utilization report), we will need to turn on metrics for
                    // every storage account here.
                    string asyncQueueName = newConfig.GeneralSettings[DashConfiguration.KeyWorkerQueueName];
                    // Work out the list of accounts to import
                    var newConfigSettings = newConfig.AccountSettings
                        .Select(setting => Tuple.Create(setting.Key, setting.Value))
                        .Concat(newConfig.GeneralSettings
                            .Select(setting => Tuple.Create(setting.Key, setting.Value)))
                        .Concat(newConfig.ScaleAccounts.Accounts
                            .Select((account, index) => Tuple.Create(String.Format("{0}{1}", DashConfiguration.KeyScaleoutAccountPrefix, index), GenerateConnectionString(account))))
                        .ToDictionary(setting => setting.Item1, setting => setting.Item2, StringComparer.OrdinalIgnoreCase);
                    var scaleoutAccounts = serviceSettings
                        .Where(AzureServiceConfiguration.SettingPredicateScaleoutStorage)
                        .Select(account => ParseConnectionString(account.Item2))
                        .Where(account => account != null)
                        .ToDictionary(account => account.AccountName, StringComparer.OrdinalIgnoreCase);
                    var importAccounts = newConfig.ScaleAccounts.Accounts
                        .Where(newAccount => !scaleoutAccounts.ContainsKey(newAccount.AccountName))
                        .ToList();
                    // Prepare the new accounts, import accounts & service configuration information into a message for the async worker.
                    // Despite kicking the async worker off directly here, we need the message durably enqueued so that any downstream
                    // failures will be retried.
                    var rdfeAccessToken = await GetRdfeRefreshToken();
                    operationStatus.AccountsToBeImported = importAccounts
                        .Select(account => account.AccountName)
                        .ToList();
                    var message = new QueueMessage(MessageTypes.UpdateService, 
                        new Dictionary<string, string>
                        {
                            { UpdateServicePayload.OperationId, operationId },
                            { UpdateServicePayload.SubscriptionId, serviceClient.SubscriptionId },
                            { UpdateServicePayload.ServiceName, serviceClient.ServiceName },
                            { UpdateServicePayload.RefreshToken, rdfeAccessToken },
                        },
                        DashTrace.CorrelationId);
                    var messageWrapper = new UpdateServicePayload(message);
                    messageWrapper.CreateAccountRequestIds = operationStatus.AccountsToBeCreated;
                    messageWrapper.ImportAccounts = importAccounts
                        .Select(account => GenerateConnectionString(account));
                    messageWrapper.Settings = newConfigSettings;
                    // Post message to the new namespace account (it may have changed) as that is where the async workers will read from after update
                    await new AzureMessageQueue(namespaceAccount, asyncQueueName).EnqueueAsync(message, 0);
                    // Manually fire up the async worker (so that we can supply it with the new namespace account)
                    var queueTask = ProcessOperationMessageLoop(operationId, namespaceAccount, GenerateConnectionString(namespaceAccount), GetMessageDelay(), asyncQueueName);
                    newConfig.OperationId = operationId;
                    newConfig.ScaleAccounts.MaxAccounts = DashConfiguration.MaxDataAccounts; 
                    return Content(HttpStatusCode.Accepted, newConfig);
                }
                catch (Exception ex)
                {
                    if (operationStatus != null)
                    {
                        var task = operationStatus.UpdateStatus(UpdateConfigStatus.States.Failed, ex.ToString());
                    }
                    throw;
                }
            });
        }
コード例 #6
0
ファイル: AzureMessageQueue.cs プロジェクト: farukc/Dash
 public async Task EnqueueAsync(QueueMessage payload, int? initialInvisibilityDelay = null)
 {
     CloudQueueMessage message = new CloudQueueMessage(payload.ToJson());
     TimeSpan? invisibilityDelay = TimeSpan.FromSeconds(initialInvisibilityDelay ?? DashConfiguration.WorkerQueueInitialDelay);
     await this.Queue.AddMessageAsync(message, null, invisibilityDelay, null, null);
 }
コード例 #7
0
ファイル: AzureMessageQueue.cs プロジェクト: farukc/Dash
 public void Enqueue(QueueMessage payload, int? initialInvisibilityDelay = null)
 {
     EnqueueAsync(payload, initialInvisibilityDelay).Wait();
 }
コード例 #8
0
ファイル: AzureMessageQueue.cs プロジェクト: farukc/Dash
 public AzureMessageQueue(QueueMessage sourceMessage)
 {
     this.Queue = ((AzureMessageItem)sourceMessage.MessageItem).ContainingQueue.Queue;
 }
コード例 #9
0
 public static QueueMessage ConstructReplicationMessage(bool deleteReplica, string sourceAccount, string destinationAccount, string container, string blob, string destinationETag)
 {
     var retval = new QueueMessage(deleteReplica ? MessageTypes.DeleteReplica : MessageTypes.BeginReplicate, 
         new Dictionary<string, string> 
         {
             { ReplicatePayload.Source, deleteReplica ? destinationAccount : sourceAccount },
             { ReplicatePayload.Destination, destinationAccount },
             { ReplicatePayload.Container, container },
             { ReplicatePayload.BlobName, blob },
         },
         DashTrace.CorrelationId);
     if (deleteReplica)
     {
         retval.Payload[DeleteReplicaPayload.ETag] = destinationETag;
     }
     return retval;
 }
コード例 #10
0
ファイル: MessageProcessor.cs プロジェクト: farukc/Dash
 static void EnqueueServiceOperationUpdate(QueueMessage message, int? invisibilityTimeout = null)
 {
     if (message.AbandonOperation)
     {
         return;
     }
     // Ensure that this message isn't visible immediately as we'll sit in a tight loop
     new AzureMessageQueue(message).Enqueue(new QueueMessage(MessageTypes.ServiceOperationUpdate,
         new Dictionary<string, string>
         {
             { UpdateServicePayload.OperationId, message.Payload[UpdateServicePayload.OperationId] },
             { UpdateServicePayload.SubscriptionId, message.Payload[ServiceOperationPayload.SubscriptionId] },
             { UpdateServicePayload.ServiceName, message.Payload[ServiceOperationPayload.ServiceName] },
             { UpdateServicePayload.RefreshToken, message.Payload[ServiceOperationPayload.RefreshToken] },
         },
         message.CorrelationId), invisibilityTimeout ?? 10);
 }
コード例 #11
0
ファイル: MessageProcessor.cs プロジェクト: farukc/Dash
 static bool DoServiceOperationUpdate(QueueMessage message, int? invisibilityTimeout = null)
 {
     if (ServiceUpdater.UpdateOperationStatus(
         message.Payload[ServiceOperationPayload.SubscriptionId],
         message.Payload[ServiceOperationPayload.ServiceName],
         message.Payload[ServiceOperationPayload.OperationId],
         message.Payload[ServiceOperationPayload.RefreshToken],
         message.AbandonOperation).Result && !message.AbandonOperation)
     {
         // Re-enqueue another message to continue to check on the operation
         EnqueueServiceOperationUpdate(message, invisibilityTimeout);
     }
     return true;
 }
コード例 #12
0
ファイル: MessageProcessor.cs プロジェクト: farukc/Dash
 static bool DoUpdateServiceJob(QueueMessage message, int? invisibilityTimeout = null)
 {
     // Extend the timeout on this message (5 mins for account creation, 1 min for account import & 30 secs to upgrade service)
     message.UpdateInvisibility(invisibilityTimeout ?? 390);
     var payload = new UpdateServicePayload(message);
     string operationId = ServiceUpdater.ImportAccountsAndUpdateService(
         message.Payload[ServiceOperationPayload.SubscriptionId],
         message.Payload[ServiceOperationPayload.ServiceName],
         message.Payload[ServiceOperationPayload.OperationId],
         message.Payload[ServiceOperationPayload.RefreshToken],
         payload.CreateAccountRequestIds,
         payload.ImportAccounts,
         payload.Settings,
         message.AbandonOperation).Result;
     if (!String.IsNullOrWhiteSpace(operationId) && !message.AbandonOperation)
     {
         // Enqueue a follow-up message to check the progress of this operation
         EnqueueServiceOperationUpdate(message, invisibilityTimeout);
         return true;
     }
     return false;
 }
コード例 #13
0
ファイル: MessageProcessor.cs プロジェクト: farukc/Dash
 static bool DoDeleteReplicaJob(QueueMessage message, int? invisibilityTimeout = null)
 {
     if (message.AbandonOperation)
     {
         // We don't have any specific failure functionality for this operation
         return true;
     }
     return BlobReplicator.DeleteReplica(
         message.Payload[DeleteReplicaPayload.Source],
         message.Payload[DeleteReplicaPayload.Container],
         message.Payload[DeleteReplicaPayload.BlobName],
         message.Payload[DeleteReplicaPayload.ETag]);
 }
コード例 #14
0
ファイル: MessageProcessor.cs プロジェクト: farukc/Dash
 static bool DoReplicateProgressJob(QueueMessage message, int? invisibilityTimeout = null)
 {
     if (message.AbandonOperation)
     {
         // We don't have any specific failure functionality for this operation
         return true;
     }
     return BlobReplicator.ProgressBlobReplication(
         message.Payload[ReplicateProgressPayload.Source],
         message.Payload[ReplicateProgressPayload.Destination],
         message.Payload[ReplicateProgressPayload.Container],
         message.Payload[ReplicateProgressPayload.BlobName],
         message.Payload[ReplicateProgressPayload.CopyID],
         invisibilityTimeout);
 }
コード例 #15
0
ファイル: AzureMessageQueue.cs プロジェクト: CedarLogic/Dash
 public void Enqueue(QueueMessage payload, int?initialInvisibilityDelay = null)
 {
     EnqueueAsync(payload, initialInvisibilityDelay).Wait();
 }