public async Task <bool> TryUpdateAsync(ITransaction tx, TKey key, TValue newValue, TValue comparisonValue)
        {
            if (reliableDictionary == null)
            {
                await InitializeReliableDictionary();
            }

            return(await reliableDictionary.TryUpdateAsync(tx, key, newValue, comparisonValue));
        }
        private async Task PersistChanges(Order order, OrderSnapshot initial, IReliableDictionary <Guid, OrderSnapshot> dictionary, ITransaction transaction)
        {
            var updated = await dictionary.TryUpdateAsync(transaction, order.Id, order.GetSnapshot(), initial);

            if (updated && order.IsClosed())
            {
                await dictionary.TryRemoveAsync(transaction, order.Id);
            }
        }
Beispiel #3
0
        private async Task ExecuteUpdateUserTransactionalAsync(ITransaction tx, User user, IReliableDictionary <string, User> users, CancellationToken cancellationToken)
        {
            var current = await users.TryGetValueAsync(tx, user.Id, LockMode.Update);

            if (current.HasValue)
            {
                await users.TryUpdateAsync(tx, user.Id, user, current.Value, TimeSpan.FromSeconds(15), cancellationToken);
            }
            else
            {
                throw new ApplicationException($"Cannot update non existent user '{user.Id}'");
            }
        }
        public async Task Update(string id, T entity)
        {
            var oldEntityt     = (await _aggreateStorage.TryGetValueAsync(_transaction, id)).Value;
            var storableEntity = _mapper.MapToStorable(entity);

            var succeed =
                await _aggreateStorage.TryUpdateAsync(_transaction, entity.Id, storableEntity, oldEntityt);

            if (!succeed)
            {
                throw new Exception($"Something went wrong when trying to update the entity {id}");
            }
        }
        private async Task <bool> UpdateLastAccessedAsync(string key, CacheEntry storedEntry, IReliableDictionary <string, CacheEntry> dictionary,
                                                          TimeSpan timeout, CancellationToken cancellationToken)
        {
            var newEntry = (CacheEntry)storedEntry.Clone();

            newEntry.LastAccessed = DateTimeOffset.UtcNow;
            using (var transaction = _stateManager.CreateTransaction())
            {
                var result = await dictionary.TryUpdateAsync(transaction, key, newEntry, storedEntry, timeout, cancellationToken);

                await transaction.CommitAsync();

                return(result);
            }
        }
        /// <summary>
        ///     Registers the configuration.
        /// </summary>
        /// <param name="request">The request.</param>
        /// <returns></returns>
        public async Task <bool> RegisterConfiguration(ConsumerConfigureRequestModel request)
        {
            IReliableDictionary <string, ConsumerConfiguresState> dictionary = await this.StateManager.GetOrAddAsync <IReliableDictionary <string, ConsumerConfiguresState> >(DictionaryKey);

            using (ITransaction tx = this.StateManager.CreateTransaction())
            {
                ConditionalValue <ConsumerConfiguresState> result = await dictionary.TryGetValueAsync(tx, ConfigureKey);

                bool isSuc;
                if (result.HasValue)
                {
                    int count = result.Value.Configures.Count(x => x.Action.Equals(request.Action) && x.ServiceName.Equals(request.ServiceName) && x.DictionaryKey.Equals(request.DictionaryKey));
                    if (count > 0)
                    {
                        return(false);
                    }
                    ConsumerConfiguresState configure = result.Value;
                    configure.Configures.Add(new ConsumerConfigureInfo
                    {
                        Action        = request.Action,
                        Address       = request.Address,
                        ServiceName   = request.ServiceName,
                        DictionaryKey = request.DictionaryKey
                    });
                    isSuc = await dictionary.TryUpdateAsync(tx, ConfigureKey, configure, result.Value);

                    return(isSuc);
                }
                ConsumerConfiguresState state = new ConsumerConfiguresState
                {
                    Configures = new List <ConsumerConfigureInfo>
                    {
                        new ConsumerConfigureInfo
                        {
                            Action        = request.Action,
                            Address       = request.Address,
                            ServiceName   = request.ServiceName,
                            DictionaryKey = request.DictionaryKey
                        }
                    }
                };
                isSuc = await dictionary.TryAddAsync(tx, ConfigureKey, state);

                await tx.CommitAsync();

                return(isSuc);
            }
        }
Beispiel #7
0
        public async Task <bool> AddOrUpdateAsync(Person person)
        {
            using (var ctx = this.StateManager.CreateTransaction())
            {
                var personId      = person.Id;
                var currentPerson = (Person)null;

                if (await _personDictionary.ContainsKeyAsync(ctx, personId))
                {
                    var personValue = await _personDictionary.TryGetValueAsync(ctx, personId);

                    if (personValue.HasValue)
                    {
                        currentPerson = personValue.Value;
                    }
                }

                if (currentPerson == null)
                {
                    await _personDictionary.AddAsync(ctx, person.Id, person);

                    if (!String.IsNullOrWhiteSpace(person.EmailAddress))
                    {
                        await _emailLookupDictionary.AddAsync(ctx, person.EmailAddress, person.Id);
                    }

                    if (!String.IsNullOrWhiteSpace(person.TwitterHandle))
                    {
                        await _twitterLookupDictionary.AddAsync(ctx, person.TwitterHandle, person.Id);
                    }

                    await ctx.CommitAsync();

                    return(true);
                }
                else
                {
                    if (await _personDictionary.TryUpdateAsync(ctx, person.Id, person, currentPerson))
                    {
                        await ctx.CommitAsync();

                        return(true);
                    }
                }
            }

            return(false);
        }
        public async Task <IActionResult> Put(int characterId)
        {
            try
            {
                IReliableDictionary <int, Character> characterDictionary =
                    await _stateManager.GetOrAddAsync <IReliableDictionary <int, Character> >(CharactersName);

                using (ITransaction tx = _stateManager.CreateTransaction())
                {
                    var characterConditionalValue = await characterDictionary.TryGetValueAsync(tx, characterId);

                    if (!characterConditionalValue.HasValue)
                    {
                        return(new BadRequestResult());
                    }

                    var character = characterConditionalValue.Value;

                    // this is invalid, it will not update the replicas
                    // character.Votes++;

                    var cloned = (Character)character.Clone();
                    cloned.Votes += 2;

                    await characterDictionary.TryUpdateAsync(tx, characterId, cloned, character);

                    // alternative
                    //await characterDictionary.AddOrUpdateAsync(tx, characterId, character, (value, oldvalue) =>
                    //{
                    //    oldvalue.Votes++;
                    //    return oldvalue;
                    //});

                    await tx.CommitAsync();
                }

                return(new OkResult());
            }
            catch
            {
                return(new BadRequestResult());
            }
        }
Beispiel #9
0
        private async Task <bool> ExecuteUpdateUserAsync(User user, IReliableDictionary <string, User> users, CancellationToken cancellationToken)
        {
            bool result;

            using (var tx = this.StateManager.CreateTransaction())
            {
                var current = await users.TryGetValueAsync(tx, user.Id, LockMode.Update);

                if (current.HasValue)
                {
                    result = await users.TryUpdateAsync(tx, user.Id, user, current.Value, TimeSpan.FromSeconds(15), cancellationToken);

                    await tx.CommitAsync();

                    MetricsLog?.UserUpdated(user);
                }
                else
                {
                    throw new ApplicationException($"Cannot update non existent user '{user.Id}'");
                }
            }
            return(result);
        }
        /// <summary>
        /// Performs a HealthCheck for a scheduled item.
        /// </summary>
        /// <param name="item">WatchdogScheduledItem instance.</param>
        internal async Task PerformItemHealthCheckAsync(WatchdogScheduledItem item)
        {
            // Get the health check dictionaries.
            IReliableDictionary <string, HealthCheck> dict = await this.GetHealthCheckDictionaryAsync();

            IReliableDictionary <long, WatchdogScheduledItem> scheduleDict = await this.GetHealthCheckScheduleDictionaryAsync();

            // Create a transaction.
            using (ITransaction tx = this._service.StateManager.CreateTransaction())
            {
                // Attempt to get the HealthCheck instance for the key. If not return.
                ConditionalValue <HealthCheck> cv = await dict.TryGetValueAsync(tx, item.Key, LockMode.Update);

                if (cv.HasValue)
                {
                    HealthCheck hc = cv.Value;

                    try
                    {
                        // Find the partition information that matches the partition identifier.
                        // If the partition isn't found, remove the health check item.
                        Partition partition = await this.FindMatchingPartitionAsync(hc.Partition);

                        if (null == partition)
                        {
                            await dict.TryRemoveAsync(tx, hc.Key, this._timeout, this._token);
                        }
                        else
                        {
                            // Execute the check and evaluate the results returned in the new HealthCheck instance.
                            hc = await this.ExecuteHealthCheckAsync(hc, partition);

                            // Update the value of the HealthCheck to store the results of the test.
                            await dict.TryUpdateAsync(tx, item.Key, hc, cv.Value);

                            // Remove the current scheduled item.
                            await scheduleDict.TryRemoveAsync(tx, item.ExecutionTicks);

                            // Add the new scheduled item.
                            WatchdogScheduledItem newItem = new WatchdogScheduledItem(hc.LastAttempt.Add(hc.Frequency), hc.Key);
                            await(scheduleDict.TryAddAsync(tx, newItem.ExecutionTicks, newItem));
                        }

                        // Commit the transaction.
                        await tx.CommitAsync();
                    }
                    catch (TimeoutException ex)
                    {
                        ServiceEventSource.Current.ServiceMessage(this._service.Context, ex.Message);
                    }
                    catch (FabricNotPrimaryException ex)
                    {
                        ServiceEventSource.Current.ServiceMessage(this._service.Context, ex.Message);
                        return;
                    }
                    catch (Exception ex)
                    {
                        ServiceEventSource.Current.ServiceMessage(this._service.Context, ex.Message);
                        throw;
                    }
                }
            }
        }
        public async Task <string> OrchestrateWorker(WorkerDescription workerDescription)
        {
            if (_processorDictionary == null)
            {
                this._processorDictionary = this.StateManager
                                            .GetOrAddAsync <IReliableDictionary <string, ProcessorInformation> >("orchestrator.ProcessorDictionary").Result;
            }

            ServiceEventSource.Current.ServiceMessage(this.Context, $"Orchestrate worker called for {workerDescription.Identifier}");

            var address = String.Empty;

            using (var tx = this.StateManager.CreateTransaction())
            {
                ConditionalValue <ProcessorInformation> result = new ConditionalValue <ProcessorInformation>(false, null);

                int retryAttempt = 0;

getProcessorInfo:
                try
                {
                    result = await _processorDictionary.TryGetValueAsync(tx, workerDescription.Identifier);
                }
                catch (TimeoutException)
                {
                    // see below for explanation
                    if (retryAttempt++ <= 5)
                    {
                        goto getProcessorInfo;
                    }
                    else
                    {
                        throw;
                    }
                }

                if (result.HasValue)
                {
                    var info = result.Value;

                    retryAttempt = 0; // reset
                    // when running on "slow" machines, if the incoming data is bad and incorrectly partitoned
                    // we will run into time-outs, therefore it is wise to retry the operation, but we'll limit
                    // it to 5 retry attempts
updateRetry:
                    try
                    {
                        await _processorDictionary.TryUpdateAsync(tx, workerDescription.Identifier,
                                                                  new ProcessorInformation()
                        {
                            Address          = info.Address,
                            TicksLastUpdated = DateTime.UtcNow.Ticks
                        },
                                                                  info);

                        await tx.CommitAsync();
                    }
                    catch (TimeoutException)
                    {
                        retryAttempt++;
                        if (retryAttempt >= 5)
                        {
                            throw;
                        }

                        await Task.Delay(100);

                        goto updateRetry;
                    }

                    address = info.Address;
                }
                else
                {
                    // spin up the new service here
                    ServiceEventSource.Current.ServiceMessage(this.Context, $"Creating processor for {workerDescription.Identifier}");

                    var appName = Context.CodePackageActivationContext.ApplicationName;
                    var svcName = $"{appName}/{Names.ProcessorSuffix}/{workerDescription.Identifier}";
                    address = svcName;

                    try
                    {
                        await _fabricClient.ServiceManager.CreateServiceAsync(new StatefulServiceDescription()
                        {
                            HasPersistedState          = true,
                            PartitionSchemeDescription = new UniformInt64RangePartitionSchemeDescription(1),
                            ServiceTypeName            = Names.ProcessorTypeName,
                            ApplicationName            = new System.Uri(appName),
                            ServiceName = new System.Uri(svcName)
                        });

                        ServiceEventSource.Current.ServiceMessage(this.Context, $"Processor for {workerDescription.Identifier} running on {svcName}");


                        retryAttempt = 0;
svcToDictionaryAdd:
                        try
                        {
                            await _processorDictionary.AddAsync(tx, workerDescription.Identifier, new ProcessorInformation()
                            {
                                Address          = svcName,
                                TicksLastUpdated = DateTime.UtcNow.Ticks
                            });

                            await tx.CommitAsync();
                        }
                        catch (TimeoutException)
                        {
                            retryAttempt++;
                            if (retryAttempt >= 5)
                            {
                                throw;
                            }

                            await Task.Delay(100);

                            // see above for explanation
                            goto svcToDictionaryAdd;
                        }
                    }
                    catch (FabricElementAlreadyExistsException)
                    {
                        // this is a weird case, that happens if the same ID was sent to multiple
                        ServiceEventSource.Current.ServiceMessage(this.Context, $"Processor already existed for {workerDescription.Identifier} on {svcName}");
                        tx.Abort();
                    }
                }
            }

            return(address);
        }
Beispiel #12
0
        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);
            }
        }
        public async Task <string> OrchestrateWorker(WorkerDescription workerDescription)
        {
            if (_processorDictionary == null)
            {
                this._processorDictionary = this.StateManager
                                            .GetOrAddAsync <IReliableDictionary <string, ProcessorInformation> >("orchestrator.ProcessorDictionary").Result;
            }

            ServiceEventSource.Current.ServiceMessage(this.Context, $"Orchestrate worker called for {workerDescription.Identifier}");

            var address = String.Empty;

            using (var tx = this.StateManager.CreateTransaction())
            {
                var result = await _processorDictionary.TryGetValueAsync(tx, workerDescription.Identifier);

                if (result.HasValue)
                {
                    var info = result.Value;

                    await _processorDictionary.TryUpdateAsync(tx, workerDescription.Identifier,
                                                              new ProcessorInformation()
                    {
                        Address          = info.Address,
                        TicksLastUpdated = DateTime.UtcNow.Ticks
                    },
                                                              info);

                    await tx.CommitAsync();

                    address = info.Address;
                }
                else
                {
                    // spin up the new service here
                    ServiceEventSource.Current.ServiceMessage(this.Context, $"Creating processor for {workerDescription.Identifier}");

                    var appName = Context.CodePackageActivationContext.ApplicationName;
                    var svcName = $"{appName}/{Names.ProcessorSuffix}/{workerDescription.Identifier}";

                    await _fabricClient.ServiceManager.CreateServiceAsync(new StatefulServiceDescription()
                    {
                        HasPersistedState          = true,
                        PartitionSchemeDescription = new UniformInt64RangePartitionSchemeDescription(1),
                        ServiceTypeName            = Names.ProcessorTypeName,
                        ApplicationName            = new System.Uri(appName),
                        ServiceName = new System.Uri(svcName)
                    });

                    ServiceEventSource.Current.ServiceMessage(this.Context, $"Processor for {workerDescription.Identifier} running on {svcName}");

                    await _processorDictionary.AddAsync(tx, workerDescription.Identifier, new ProcessorInformation()
                    {
                        Address          = svcName,
                        TicksLastUpdated = DateTime.UtcNow.Ticks
                    });

                    address = svcName;

                    await tx.CommitAsync();
                }
            }

            return(address);
        }