/// <summary> /// Accepts a restock request and changes the Actor's state accordingly. The request is processed /// async and the caller will be notified when the processing is done. /// </summary> /// <param name="request"></param> /// <returns></returns> public async Task AddRestockRequestAsync(RestockRequest request) { RestockRequestActorState state = await this.StateManager.GetStateAsync <RestockRequestActorState>(ActorStatePropertyName); if (state.IsStarted()) //Don't accept a request that is already started { ActorEventSource.Current.Message(string.Format("RestockRequestActor: {0}: Can't accept restock request in this state", state)); throw new InvalidOperationException(string.Format("{0}: Can't accept restock request in this state", state)); } // Accept the request ActorEventSource.Current.ActorMessage(this, "RestockRequestActor: Accept update quantity request {0}", request); state.Status = RestockRequestStatus.Accepted; state.Request = request; await this.StateManager.SetStateAsync <RestockRequestActorState>(ActorStatePropertyName, state); // Start a reminder to go through the processing pipeline. // A reminder keeps the actor from being garbage collected due to lack of use, // which works better than a timer in this case. await this.RegisterReminderAsync( RestockRequestReminderNames.RestockPipelineChangeReminderName, null, PipelineStageVerificationDelay, PipelineStageProcessingDuration); return; }
public static void Run([QueueTrigger("orders", Connection = "AzureWebJobsStorage")] string myQueueItem, TraceWriter log) { log.Info($"RestockProducts function triggered."); RestockRequest restockRequest = JsonConvert.DeserializeObject <RestockRequest>(myQueueItem); var productRepository = new ProductRepository(); productRepository.IncrementProductCount(restockRequest.ProductId, restockRequest.RestockQuantity).Wait(); }
public static async Task <HttpResponseMessage> Run([HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequestMessage req, TraceWriter log) { log.Info("Order Products function triggered."); // Retrieve the order request details var order = await req.Content.ReadAsAsync <OrderRequest>(); if (order == null) { return(req.CreateResponse(HttpStatusCode.BadRequest, "Missing order details.")); } // Retrieve the product var productRepository = new ProductRepository(); var product = productRepository.GetProductById(order.Item.ProductId); // If the product does not exist return a meaningful response if (product == null) { var response = new OrderResponse { OrderStatus = "Unsuccessful", Message = "Product not found" }; return(req.CreateResponse(HttpStatusCode.OK, response)); } // Check to see if there is enough inventory var orderResponse = new OrderResponse() { OrderStatus = "Success" }; if (product.Count >= order.Item.Quantity) { orderResponse.Message = "Your product will be shipped within 5 business days."; } else { orderResponse.Message = "Your product will be shipped within 10 business days."; // Place an item into a queue to manufacture more products. var restock = new RestockRequest { ProductId = product.Id, RestockQuantity = order.Item.Quantity * 2 }; await SendRestockMessageToQueue(JsonConvert.SerializeObject(restock)); } // Decrement the inventory count productRepository.DecrementProductCount(product.Id, order.Item.Quantity).Wait(); return(req.CreateResponse(HttpStatusCode.OK, orderResponse)); }
/// <summary> /// This method uses an IReliableQueue to store completed RestockRequests which are later sent to the client using batch processing. /// We could send the request immediately but we prefer to minimize traffic back to the Inventory Service by batching multiple requests /// in one trip. /// </summary> /// <param name="actorId"></param> /// <param name="request"></param> public async void RestockRequestCompleted(ActorId actorId, RestockRequest request) { IReliableQueue <RestockRequest> completedRequests = await this.StateManager.GetOrAddAsync <IReliableQueue <RestockRequest> >(CompletedRequestsQueueName); using (ITransaction tx = this.StateManager.CreateTransaction()) { await completedRequests.EnqueueAsync(tx, request); await tx.CommitAsync(); } IRestockRequestActor restockRequestActor = ActorProxy.Create <IRestockRequestActor>(actorId, this.ApplicationName); await restockRequestActor.UnsubscribeAsync <IRestockRequestEvents>(this); //QUESTION:What does this method do? }
/// <summary> /// This method activates an actor to fulfill the RestockRequest. /// </summary> /// <param name="request"></param> /// <returns></returns> public async Task AddRestockRequestAsync(RestockRequest request) { try { //Get dictionary of Restock Requests IReliableDictionary <InventoryItemId, ActorId> requestDictionary = await this.StateManager.GetOrAddAsync <IReliableDictionary <InventoryItemId, ActorId> >(ItemIdToActorIdMapName); ActorId actorId = ActorId.CreateRandom(); try { using (ITransaction tx = this.StateManager.CreateTransaction()) { await requestDictionary.AddAsync(tx, request.ItemId, actorId); await tx.CommitAsync(); } } catch (ArgumentException) { // restock request already exists return; } // Create actor proxy and send the request IRestockRequestActor restockRequestActor = ActorProxy.Create <IRestockRequestActor>(actorId, this.ApplicationName); await restockRequestActor.AddRestockRequestAsync(request); // Successfully added, register for event notifications for completion await restockRequestActor.SubscribeAsync <IRestockRequestEvents>(this); ServiceEventSource.Current.ServiceMessage(this, "Created restock request. Item ID: {0}. Actor ID: {1}", request.ItemId, actorId); } catch (InvalidOperationException ex) { ServiceEventSource.Current.Message(string.Format("RestockRequestManagerService: Actor rejected {0}: {1}", request, ex)); throw; } catch (Exception ex) { ServiceEventSource.Current.Message(string.Format("RestockRequestManagerService: Exception {0}: {1}", request, ex)); throw; } }
private static async void AddRestockRequestsAsync() { // TODO: need to go to correct partition // For now, the inventory is not partitioned, so always go to first partition IRestockRequestManager restockRequestService = ServiceProxy.Create <IRestockRequestManager>(0, RestockRequestManagerServiceName); IList <Task> tasks = new List <Task>(); for (int i = 0; i < ParallelRequests; i++) { RestockRequest request = GenerateRandomRequest(); Console.WriteLine("Add request {0}", request); tasks.Add(restockRequestService.AddRestockRequestAsync(request)); } await Task.WhenAll(tasks); }
private async Task PeriodicInventoryCheck(CancellationToken cancellationToken) { IReliableDictionary <InventoryItemId, InventoryItem> inventoryItems = await this.stateManager.GetOrAddAsync <IReliableDictionary <InventoryItemId, InventoryItem> >(InventoryItemDictionaryName); if (this.storageType == StorageTypes.Azure) { await this.InitializeAsync(cancellationToken); } while (!cancellationToken.IsCancellationRequested) { ServiceEventSource.Current.ServiceMessage(this, "Checking inventory stock for {0} items.", await inventoryItems.GetCountAsync()); foreach (InventoryItem item in inventoryItems.Select(x => x.Value)) { cancellationToken.ThrowIfCancellationRequested(); try { //Check if stock is below restockThreshold and if the item is not already on reorder if ((item.AvailableStock <= item.RestockThreshold) && !item.OnReorder) { ServiceUriBuilder builder = new ServiceUriBuilder(RestockRequestManagerServiceName); IRestockRequestManager restockRequestManagerClient = ServiceProxy.Create <IRestockRequestManager>(0, builder.ToUri()); // we reduce the quantity passed in to RestockRequest to ensure we don't overorder RestockRequest newRequest = new RestockRequest(item.Id, (item.MaxStockThreshold - item.AvailableStock)); InventoryItem updatedItem = new InventoryItem( item.Description, item.Price, item.AvailableStock, item.RestockThreshold, item.MaxStockThreshold, item.Id, true); // TODO: this call needs to be idempotent in case we fail to update the InventoryItem after this completes. await restockRequestManagerClient.AddRestockRequestAsync(newRequest); // Write operations take an exclusive lock on an item, which means we can't do anything else with that item while the transaction is open. // If something blocks before the transaction is committed, the open transaction on the item will prevent all operations on it, including reads. // Once the transaction commits, the lock is released and other operations on the item can proceed. // Operations on the transaction all have timeouts to prevent deadlocking an item, // but we should do as little work inside the transaction as possible that is not related to the transaction itself. using (ITransaction tx = this.stateManager.CreateTransaction()) { await inventoryItems.TryUpdateAsync(tx, item.Id, updatedItem, item); await tx.CommitAsync(); } ServiceEventSource.Current.ServiceMessage( this, "Restock order placed. Item ID: {0}. Quantity: {1}", newRequest.ItemId, newRequest.Quantity); } } catch (Exception e) { ServiceEventSource.Current.ServiceMessage(this, "Failed to place restock order for item {0}. {1}", item.Id, e.ToString()); } if (this.storageType == StorageTypes.Local) { await this.StateManager.BackupAsync(this.BackupCallbackAsync); } else { await this.StateManager.BackupAsync(this.BackupCallbackAzureAsync); } } await Task.Delay(TimeSpan.FromSeconds(30), cancellationToken); } }