public static async Task <Chirp[]> RunOrchestrator( [OrchestrationTrigger] IDurableOrchestrationContext context) { var userId = context.GetInput <string>(); // call the UserFollows entity to figure out whose chirps should be included var userFollowsProxy = context.CreateEntityProxy <IUserFollows>(userId); var followedUsers = await userFollowsProxy.Get(); // in parallel, collect all the chirps var tasks = followedUsers .Select(id => context .CreateEntityProxy <IUserChirps>(id) .Get()) .ToList(); await Task.WhenAll(tasks); // combine and sort the returned lists of chirps var sortedResults = tasks .SelectMany(task => task.Result) .OrderBy(chirp => chirp.Timestamp); return(sortedResults.ToArray()); }
public static async Task <bool> TransferFun( [OrchestrationTrigger] IDurableOrchestrationContext ctx ) { var input = ctx.GetInput <TransferArgs>(); var fromEntity = new EntityId(nameof(Account), input.FromAccount); var toEntity = new EntityId(nameof(Account), input.ToAccount); using (await ctx.LockAsync(fromEntity, toEntity)) { var fromAccount = ctx.CreateEntityProxy <IAccount>(fromEntity); var toAccount = ctx.CreateEntityProxy <IAccount>(toEntity); var hasEnoughFunds = await fromAccount.Withdraw(input.Amount); if (!hasEnoughFunds) { return(false); } await toAccount.Replenish(input.Amount); } return(true); }
public async Task Run( [OrchestrationTrigger] IDurableOrchestrationContext context) { var proxy = context.CreateEntityProxy <IEntitiesAggregator>(new EntityId(nameof(EntitiesAggregator), EntitiesAggregator.EntityId)); var entities = await proxy.GetWorkedServices(); var tasks = new List <Task>(); foreach (var entity in entities) { var entityId = new EntityId(nameof(ServiceAggregator), entity); var serviceProxy = context.CreateEntityProxy <IServiceAggregator>(entityId); var service = await serviceProxy.Get(); tasks.Add( context.CallSubOrchestratorAsync(nameof(HealCheckOrchestration), new ServiceInput { Id = entity, Name = service.Name, Url = service.Url })); } await Task.WhenAll(tasks); }
public static async Task <bool> RunOrchestrator( [OrchestrationTrigger] IDurableOrchestrationContext context) { var userId = context.GetInput <string>(); var shoppingCartEntity = new EntityId(nameof(ShoppingCartEntity), userId); var inventoryEntity = new EntityId(nameof(InventoryEntity), "onestore"); var orderEntity = new EntityId(nameof(OrderEntity), userId); // Create a critical section to avoid race conditions. using (await context.LockAsync(inventoryEntity, orderEntity, shoppingCartEntity)) { IShoppingCart shoppingCartProxy = context.CreateEntityProxy <IShoppingCart>(shoppingCartEntity); IInventory inventoryProxy = context.CreateEntityProxy <IInventory>(inventoryEntity); IOrder orderProxy = context.CreateEntityProxy <IOrder>(orderEntity); var shoppingCartItems = await shoppingCartProxy.GetItemsAsync(); var orderItem = new OrderItem() { Timestamp = DateTime.UtcNow, UserId = userId, Details = shoppingCartItems }; var canSell = true; foreach (var inventoryItem in orderItem.Details) { if (await inventoryProxy.IsItemInInventory(inventoryItem)) { await inventoryProxy.RemoveStockAsync(inventoryItem); await shoppingCartProxy.RemoveItemAsync(inventoryItem); } else { canSell = false; break; } } if (canSell) { await orderProxy.AddAsync(orderItem); // order placed successfully return(true); } // the order failed due to insufficient stock return(false); } }
public static async Task <bool> TryFinalizeMatch(string initiator, string candidate, IDurableOrchestrationContext context) { var initiatorEntity = new EntityId(nameof(UserEntity), initiator); var candidateEntity = new EntityId(nameof(UserEntity), candidate); // Check if both users are still available and close enough. // To prevent race conditions, we do this in a critical section // that locks both users. using (await context.LockAsync(initiatorEntity, candidateEntity)) { var initiatorProxy = context.CreateEntityProxy <IUserEntity>(initiatorEntity); var candidateProxy = context.CreateEntityProxy <IUserEntity>(candidateEntity); var initiatorInfo = await initiatorProxy.GetState(); var candidateInfo = await candidateProxy.GetState(); if (initiatorInfo.Location == null) { // initiator is no longer trying to find a match! No need to keep trying. return(true); } if (candidateInfo.Location == null || !ZipCodes.GetProximityList(initiatorInfo.Location.Value).Contains(candidateInfo.Location.Value)) { // candidate is no longer eligible return(false); } // match was successful. Create a new ride. var driver = initiator.StartsWith("D") ? initiatorInfo : candidateInfo; var rider = initiator.StartsWith("R") ? initiatorInfo : candidateInfo; var rideInfo = new RideInfo() { RideId = context.NewGuid(), DriverId = driver.UserId, DriverLocation = driver.Location.Value, RiderId = rider.UserId, RiderLocation = rider.Location.Value, }; // assign both users to the new ride. // (this is happening within the critical section) await Task.WhenAll(initiatorProxy.SetRide(rideInfo), candidateProxy.SetRide(rideInfo)); return(true); } }
public static async Task RunOrchestrator( [OrchestrationTrigger] IDurableOrchestrationContext context) { var(userId, location) = context.GetInput <(string, int)>(); var userEntity = new EntityId(nameof(UserEntity), userId); // first, update the user information to advertise the location. await context.CallEntityAsync(userEntity, nameof(UserEntity.SetLocation), location); // next, try to match this user with // someone who has already advertised their location in a nearby region var nearbyRegions = ZipCodes.GetProximityList(location); foreach (var region in nearbyRegions) { var regionProxy = context.CreateEntityProxy <IRegionEntity>(region.ToString()); string[] candidates = await(userId.StartsWith("R") ? regionProxy.GetAvailableDrivers() : regionProxy.GetAvailableRiders()); foreach (var candidate in candidates) { if (await TryFinalizeMatch(userId, candidate, context)) { return; } } } // we could not find a match. // we will just wait until someone else finds us. }
public static async Task Run([OrchestrationTrigger] IDurableOrchestrationContext context) { var proxy = context.CreateEntityProxy <IDemoEntity>(new EntityId(nameof(DemoEntity), context.GetInput <Guid>().ToString())); await proxy.Do(); await context.CallActivityAsync(nameof(Activity), null); }
public static async Task <NewEmployeeRequest> RunOrchestrator( [OrchestrationTrigger] IDurableOrchestrationContext context, ILogger log) { var employeeRuest = new NewEmployeeRequest { EmployeeFullName = new Faker().Name.FullName() }; employeeRuest = await context.CallActivityAsync <NewEmployeeRequest>("GenerateEmployeeId", employeeRuest); employeeRuest = await context.CallActivityAsync <NewEmployeeRequest>("AssignManager", employeeRuest); string remarks; using (var timeoutCts = new CancellationTokenSource()) { var dueTime = context.CurrentUtcDateTime.AddSeconds(300); Task durableTimeout = context.CreateTimer(dueTime, timeoutCts.Token); remarks = $"Waiting for Approval for Employee:{employeeRuest.EmployeeFullName} from the assigned manager: [{employeeRuest.ManagerFullName}]"; employeeRuest.Remarks = remarks; log.LogInformation(remarks); var entityId = new EntityId(nameof(EmployeeCounter), nameof(EmployeeCounter)); var proxy = context.CreateEntityProxy <IEmployeeCounter>(entityId); proxy.IncrementWaitingForApproval(); context.SetCustomStatus(remarks); Task <bool> approvalEvent = context.WaitForExternalEvent <bool>("ApprovalEvent"); if (approvalEvent == await Task.WhenAny(approvalEvent, durableTimeout)) { timeoutCts.Cancel(); remarks = $"Approval for employee[{employeeRuest.EmployeeFullName}] received from the assigned manager: [{employeeRuest.ManagerFullName}]"; proxy.IncrementEmployee(); context.SetCustomStatus(remarks); log.LogInformation(remarks); employeeRuest.ManagerApproved = true; } else { remarks = $"Approval was not received from the assigned manager: [{employeeRuest.ManagerFullName}]. Please escalate"; context.SetCustomStatus(remarks); log.LogInformation(remarks); employeeRuest.ManagerApproved = false; employeeRuest.Remarks = remarks; return(employeeRuest); } } employeeRuest = await context.CallActivityAsync <NewEmployeeRequest>("RegisterForGroupInsurance", employeeRuest); employeeRuest = await context.CallActivityAsync <NewEmployeeRequest>("PrintBadge", employeeRuest); remarks = $"All formalities completed for Employee {employeeRuest.EmployeeFullName}, ID: {employeeRuest.EmployeeId}"; context.SetCustomStatus(remarks); employeeRuest.Remarks = remarks; return(employeeRuest); }
public async Task <List <string> > RunOrchestrator( [OrchestrationTrigger] IDurableOrchestrationContext context) { using var disposable = context.PushLoggerProperties(); var phone = context.GetInput <string>(); var outputs = new List <string>(); var proxy = context.CreateEntityProxy <INotifySubscriptionEntity>(NotifySubscriptionEntity.BuildId(phone, EventNameApproved)); try { await proxy.Subscribe(context.InstanceId); await context.CallActivityWithRetryAsync(nameof(SendMessageActivity), _retryOptions, phone); // might fail, better include retry mechanism var approvalNotification = await proxy.GetLatestNotification() // get latest, maybe already approved (optional) ?? await WaitForUserAction(context, _timeToWait, EventNameApproved); // wait for user approval outputs.Add(approvalNotification != null ? $"We have approval from {phone} at {approvalNotification.Timestamp}" : $"No approval from {phone} after waiting {_timeToWait}"); } finally { await proxy.Unsubscribe(context.InstanceId); } return(outputs); }
public async Task ExecuteTransfer([OrchestrationTrigger] IDurableOrchestrationContext context) { var message = context.GetInput <TransferMessage>(); var fromAccountEntity = new EntityId(nameof(AccountEntity), message.FromAccountId); var toAccountEntity = new EntityId(nameof(AccountEntity), message.ToAccountId); using (await context.LockAsync(fromAccountEntity, toAccountEntity)) { var fromAccountProxy = context.CreateEntityProxy <IAccountEntity>(fromAccountEntity); var toAccountProxy = context.CreateEntityProxy <IAccountEntity>(toAccountEntity); await fromAccountProxy.Debit(new DebitAccountMessage(message.Amount)); await toAccountProxy.Credit(new CreditAccountMessage(message.Amount)); } }
private void LogEmailSent(IDurableOrchestrationContext client, CommunicationSendRequest request) { var key = new EntityId(nameof(CommsTrackingEntity), request.PersonId.ToString()); var proxy = client.CreateEntityProxy <ICommsTrackingEntity>(key); proxy.Track(request.UseCase.Id); }
public static Task <List <LoanTransaction> > ListTransaction( [OrchestrationTrigger] IDurableOrchestrationContext context) { var input = context.GetInput <object>() as dynamic; var account = input.account.Value as string; var loan = context.CreateEntityProxy <ILoan>(account); return(loan.GetTransactionHistory()); }
public static async Task <decimal> GetBalance( [OrchestrationTrigger] IDurableOrchestrationContext context) { var input = context.GetInput <object>() as dynamic; var account = input.account.Value as string; var loan = context.CreateEntityProxy <ILoan>(account); return(await loan.GetBalance()); }
public static async Task <string> EnvironmentOrchestration([OrchestrationTrigger] IDurableOrchestrationContext ctx) { var environment = ctx.GetInput <EntityId>(); var entityProxy = ctx.CreateEntityProxy <IEnvironment>(environment); // get current value return(await entityProxy.GetEnvironmentVariable(DummyEnvironmentVariable)); }
public static async Task <bool> BankTransaction([OrchestrationTrigger] IDurableOrchestrationContext context) { var iterationLength = context.GetInput <Tuple <int, int> >(); var targetAccountPair = iterationLength.Item1; var length = iterationLength.Item2; var sourceAccountId = $"src{targetAccountPair}-!{(targetAccountPair + 1) % 32:D2}"; var sourceEntity = new EntityId(nameof(Account), sourceAccountId); var destinationAccountId = $"dst{targetAccountPair}-!{(targetAccountPair + 2) % 32:D2}"; var destinationEntity = new EntityId(nameof(Account), destinationAccountId); // Add an amount to the first account var transferAmount = 1000; IAccount sourceProxy = context.CreateEntityProxy <IAccount>(sourceEntity); IAccount destinationProxy = context.CreateEntityProxy <IAccount>(destinationEntity); // we want the balance check to always succeeed to reduce noise bool forceSuccess = true; // Create a critical section to avoid race conditions. // No operations can be performed on either the source or // destination accounts until the locks are released. using (await context.LockAsync(sourceEntity, destinationEntity)) { int sourceBalance = await sourceProxy.Get(); if (sourceBalance > transferAmount || forceSuccess) { await sourceProxy.Add(-transferAmount); await destinationProxy.Add(transferAmount); return(true); } else { return(false); } } }
public static async Task <int> CounterOrchestrator( [OrchestrationTrigger] IDurableOrchestrationContext context) { var input = context.GetInput <CounterOrchestratorInput>(); ICounter counter = context.CreateEntityProxy <ICounter>(input.EntityKey); // EntityId also works as parameter. // Two-way call to the entity which awaits the response value. return(await counter.Add(input.Amount)); }
public static void InitializeLoan( [OrchestrationTrigger] IDurableOrchestrationContext context) { var input = context.GetInput <object>() as dynamic; var account = input.account.Value as string; var amount = (decimal)input.amount.Value; var loan = context.CreateEntityProxy <ILoan>(account); loan.InitializeLoan(amount); }
public async Task <ProcessedNeoEvent> Run( [OrchestrationTrigger] IDurableOrchestrationContext context, ILogger logger) { var detectedNeoEvent = context.GetInput <DetectedNeoEvent>(); var kineticEnergy = await context.CallActivityWithRetryAsync <KineticEnergyResult>( nameof(EstimateKineticEnergyActivity), GetRetryOptions(), detectedNeoEvent); var impactProbability = await context.CallActivityWithRetryAsync <ImpactProbabilityResult>( nameof(EstimateImpactProbabilityActivity), GetRetryOptions(), detectedNeoEvent); var torinoImpactRequest = TorinoImpactRequestBuilder.Build( detectedNeoEvent.Id, impactProbability.ImpactProbability, kineticEnergy.KineticEnergyInMegatonTnt); var torinoImpact = await context.CallActivityWithRetryAsync <TorinoImpactResult>( nameof(EstimateTorinoImpactActivity), GetRetryOptions(), torinoImpactRequest); var processedNeoEvent = ProcessedNeoEventBuilder.Build( detectedNeoEvent, impactProbability.ImpactProbability, kineticEnergy.KineticEnergyInMegatonTnt, torinoImpact.TorinoImpact); var proxy = context.CreateEntityProxy <IProcessedNeoEventCounter>( EntityIdBuilder.BuildForProcessedNeoEventCounter()); proxy.Add(); if (processedNeoEvent.TorinoImpact > 0) { await context.CallActivityWithRetryAsync( nameof(StoreProcessedNeoEventActivity), GetRetryOptions(), processedNeoEvent); } if (processedNeoEvent.TorinoImpact > 7) { await context.CallActivityWithRetryAsync( nameof(SendNotificationActivity), GetRetryOptions(), processedNeoEvent); } return(processedNeoEvent); }
private static async Task <List <string> > ContactDurableEntity( IDurableOrchestrationContext context) { var entity = new EntityId(nameof(EntityExample), "myEntityKey"); IEntityExample entityProxy = context.CreateEntityProxy <IEntityExample>(entity); entityProxy.AddEvent("my new event"); // signal (don't wait) as return is void. List <string> result = await entityProxy.GetEventsAsync(); // signal (wait & return) as return is a task. return(result); }
public async Task <bool> IsExecutionPermitted(string circuitBreakerId, ILogger log, IDurableOrchestrationContext orchestrationContext) { if (string.IsNullOrEmpty(circuitBreakerId)) { throw new ArgumentNullException($"{nameof(circuitBreakerId)}"); } log?.LogCircuitBreakerMessage(circuitBreakerId, $"Asking IsExecutionPermitted (consistency priority) for circuit-breaker = '{circuitBreakerId}'."); return(await orchestrationContext.CreateEntityProxy <IDurableCircuitBreaker>(circuitBreakerId).IsExecutionPermitted()); }
public static async Task <bool> WithdrawFun( [OrchestrationTrigger] IDurableOrchestrationContext ctx ) { var input = ctx.GetInput <WithdrawArgs>(); var account = ctx.CreateEntityProxy <IAccount>(input.Account); var wasSuccessful = await account.Withdraw(input.Amount); return(wasSuccessful); }
public async Task HealCheckOrchestration( [OrchestrationTrigger] IDurableOrchestrationContext context) { var service = context.GetInput <ServiceInput>(); var entityId = new EntityId(nameof(ServiceAggregator), service.Id); var serviceProxy = context.CreateEntityProxy <IServiceAggregator>(entityId); var httpClientResponse = await context.CallHttpAsync(HttpMethod.Get, new Uri(service.Url)); var status = httpClientResponse.StatusCode == HttpStatusCode.OK ? Status.Closed : Status.Open; await serviceProxy.ChangeState(new StatusCommand(status)); }
public static async Task RunOrchestrator( [OrchestrationTrigger] IDurableOrchestrationContext context) { InventoryEvent data = context.GetInput <InventoryEvent>(); var entityId = new EntityId("Store", data.StoreId); var proxy = context.CreateEntityProxy <IStore>(entityId); var itemInventory = await proxy.process(data); await context.CallActivityAsync <string>("MDSWrite", itemInventory); }
public static void AddTransaction( [OrchestrationTrigger] IDurableOrchestrationContext context) { var input = context.GetInput <object>() as dynamic; var account = input.account.Value as string; var amount = (decimal)input.amount.Value; var loan = context.CreateEntityProxy <ILoan>(account); loan.AddTransaction(new LoanTransaction { Amount = amount, TypeOfTransaction = TransactionType.Repay, TransactionTimeStamp = context.CurrentUtcDateTime }); }
public static async Task <bool> Run([OrchestrationTrigger] IDurableOrchestrationContext context, ILogger logger) { var hostId = context.GetInput <EntityId>(); logger = context.CreateReplaySafeLogger(logger); var state = await context.CallActivityAsync <EntityStateResponse <HostEntity> >(nameof(GetHostState), hostId); if (!state.EntityExists) { logger.LogError("Attempted to start watch dog for a non-existing host entity: {hostId}", hostId); throw new Exception("Failed to start watch dog for non-existing entity"); } if (context.InstanceId != HostEntity.calculateHash(state.EntityState.IpAddress)) { throw new Exception("violent suicide committed on watchdog!"); } if (state.EntityState.ipv4Support || state.EntityState.ipv6Support) { context.SignalEntity(HostList.Id, HostList.AddHost, hostId); logger.LogInformation("Adding {hostId} to active hosts", hostId); } else { context.SignalEntity(HostList.Id, HostList.RemoveHost, hostId); logger.LogInformation("Removing {hostId} from active hosts", hostId); } var nextCheck = state.EntityState.DnsUptime switch { { } v when v < 0.2m => TimeSpan.FromDays(1), { } v when v >= 0.2m && v < 0.75m => TimeSpan.FromHours(12 + new Random().Next(-1, 1)), { } v when v >= 0.75m => TimeSpan.FromMinutes(60 + new Random().Next(-5, 5)), _ => TimeSpan.FromHours(1) }; #if DEBUG nextCheck = TimeSpan.FromMinutes(5); #endif await context.CreateTimer(context.CurrentUtcDateTime + nextCheck, CancellationToken.None); var host = context.CreateEntityProxy <IHostEntity>(hostId); await host.CheckUp(); context.ContinueAsNew(hostId); return(true); } }
public static async Task TransferFundsAsync( [OrchestrationTrigger] IDurableOrchestrationContext context) { var transferCommand = context.GetInput <TransferCommand>(); if (transferCommand == null) { return; } var source = new EntityId(nameof(BankAccountEntity), transferCommand.SourceAccountId); var target = new EntityId(nameof(BankAccountEntity), transferCommand.TargetAccountId); using (await context.LockAsync(source, target)) { Console.WriteLine($"{transferCommand.SourceAccountId}: Locked"); Console.WriteLine($"{transferCommand.TargetAccountId}: Locked"); var sourceProxy = context.CreateEntityProxy <IBankAccountEntity>(source); if (await sourceProxy.GetBalanceAsync() < transferCommand.Amount) { Console.WriteLine($"{transferCommand.SourceAccountId}: Is broke. Bouncing transfer."); return; } var targetProxy = context.CreateEntityProxy <IBankAccountEntity>(target); await sourceProxy.ModifyBalanceAsync(-transferCommand.Amount); Console.WriteLine($"{transferCommand.SourceAccountId}: Taken amount out"); await targetProxy.ModifyBalanceAsync(transferCommand.Amount); Console.WriteLine($"{transferCommand.TargetAccountId}: Added amount to"); } Console.WriteLine($"{transferCommand.SourceAccountId}: Unlocked"); Console.WriteLine($"{transferCommand.TargetAccountId}: Unlocked"); }
public async Task RunOrchestrator( [OrchestrationTrigger] IDurableOrchestrationContext context, ILogger log) { _correlationInitializer.Initialize(context.InstanceId); await context.CallActivityAsync(nameof(StartReindex), null); var categoryIds = await context.CallActivityAsync <IReadOnlyCollection <string> >(nameof(ReadCategoriesToRebuild), null); var rebuildTasks = categoryIds.Select(id => context.CreateEntityProxy <ICategoryEntity>(id).Reindex()); await Task.WhenAll(rebuildTasks); log.LogProgress(OperationStatus.InProgress, "Rebuild of events stored in event store finished", context.InstanceId); categoryIds = await context.CallActivityAsync <IReadOnlyCollection <string> >(nameof(ReadCategoriesToRebuild), null); var applyEventTasks = categoryIds.Select(id => context.CreateEntityProxy <ICategoryEntity>(id).ApplyPendingEvents()); await Task.WhenAll(applyEventTasks); log.LogProgress(OperationStatus.InProgress, "Applied pending events", context.InstanceId); var deleteStateTasks = categoryIds.Select(id => context.CreateEntityProxy <ICategoryEntity>(id).Delete()); await Task.WhenAll(deleteStateTasks); await context.CallActivityAsync(nameof(FinishReindex), null); log.LogProgress(OperationStatus.Finished, string.Empty, context.InstanceId); }
public static async Task RunOrchestrator([OrchestrationTrigger] IDurableOrchestrationContext context) { var input = context.GetInput <AddItemToPlayerInventoryOrchestratorInput>(); var playerEntityId = new EntityId(nameof(Player), input.Player); var itemEntityId = new EntityId(nameof(ShopItem), input.Item); using (await context.LockAsync(playerEntityId, itemEntityId)) { var playerProxy = context.CreateEntityProxy <IPlayer>(playerEntityId); var itemProxy = context.CreateEntityProxy <IShopItem>(itemEntityId); var itemStock = await itemProxy.GetStock(); await playerProxy.AddItemToInventory(new AddItemToInventoryParams { Quantity = input.Quantity, Item = input.Item }); await itemProxy.UpdateStock(itemStock - input.Quantity); } }
public static async Task <IEnumerable <InventoryItem> > RunOrchestrator( [OrchestrationTrigger] IDurableOrchestrationContext context) { var userId = context.GetInput <string>(); var target = new EntityId(nameof(ShoppingCartEntity), userId); var shoppingCartProxy = context.CreateEntityProxy <IShoppingCart>(target); var shoppingCart = await shoppingCartProxy.GetItemsAsync(); var tasks = shoppingCart .Select(id => context .CreateEntityProxy <IInventory>(new EntityId(nameof(InventoryEntity), "onestore")) .GetItemAsync(id)) .ToList(); await Task.WhenAll(tasks); var result = tasks .Select(task => task.Result) .ToList(); return(result); }
public static async Task StartComputeSetOrchestrator( [OrchestrationTrigger] IDurableOrchestrationContext context, ILogger log) { log = context.CreateReplaySafeLogger(log); var computeSetId = context.GetInput <string>(); log.LogWarning("*** {eventName}: {computeSetId}", "ComputeSetStarting", computeSetId); // pretend to start compute set await context.CreateTimer(context.CurrentUtcDateTime.AddSeconds(5), CancellationToken.None); var computeSet = context.CreateEntityProxy <IComputeSet>(new EntityId(nameof(ComputeSet), computeSetId)); await computeSet.SignalComputeSetStarted(); }