public void CacheManager_Update_ItemDoesNotExist()
        {
            // arrange
            Func <string, string> updateFunc = s => s;
            int updateCalls = 0;
            int putCalls    = 0;
            int removeCalls = 0;

            var cache = MockHandles(
                count: 5,
                updateCalls: Enumerable.Repeat <Action>(() => updateCalls++, 5).ToArray(),
                updateCallResults: new UpdateItemResult <string>[]
            {
                UpdateItemResult.ForItemDidNotExist <string>(),
                UpdateItemResult.ForItemDidNotExist <string>(),
                UpdateItemResult.ForItemDidNotExist <string>(),
                UpdateItemResult.ForItemDidNotExist <string>(),
                UpdateItemResult.ForItemDidNotExist <string>()
            },
                putCalls: Enumerable.Repeat <Action>(() => putCalls++, 5).ToArray(),
                removeCalls: Enumerable.Repeat <Action>(() => removeCalls++, 5).ToArray());

            // act
            using (cache)
            {
                string value;
                var    updateResult = cache.TryUpdate("key", updateFunc, 1, out value);

                // assert
                updateCalls.Should().Be(1, "should exit after the first item did not exist");
                putCalls.Should().Be(0, "no put calls expected");
                removeCalls.Should().Be(4, "item should be removed from others");
                updateResult.Should().BeFalse();
            }
        }
        public async Task <UpdatePointResult> UpdatePointAsync(UpdatePointRequest updatePointRequest, CancellationToken cancellationToken = default(CancellationToken))
        {
            if (updatePointRequest == null)
            {
                throw new ArgumentNullException("updatePointRequest");
            }

            var geohash = S2Manager.GenerateGeohash(updatePointRequest.GeoPoint);
            var hashKey = S2Manager.GenerateHashKey(geohash, _config.HashKeyLength);

            var updateItemRequest = updatePointRequest.UpdateItemRequest;

            updateItemRequest.TableName = _config.TableName;

            var hashKeyValue = new AttributeValue
            {
                N = hashKey.ToString(CultureInfo.InvariantCulture)
            };

            updateItemRequest.Key[_config.HashKeyAttributeName]  = hashKeyValue;
            updateItemRequest.Key[_config.RangeKeyAttributeName] = updatePointRequest.RangeKeyValue;

            // Geohash and geoJson cannot be updated.
            updateItemRequest.AttributeUpdates.Remove(_config.GeohashAttributeName);
            updateItemRequest.AttributeUpdates.Remove(_config.GeoJsonAttributeName);

            UpdateItemResult updateItemResult = await _config.DynamoDBClient.UpdateItemAsync(updateItemRequest, cancellationToken).ConfigureAwait(false);

            var updatePointResult = new UpdatePointResult(updateItemResult);

            return(updatePointResult);
        }
        public void CacheManager_Update_Validate_LowestWins()
        {
            // arrange
            Func <string, string> updateFunc = s => s;
            int updateCalls = 0;
            int putCalls    = 0;
            int removeCalls = 0;

            var cache = MockHandles(
                count: 5,
                updateCalls: Enumerable.Repeat <Action>(() => updateCalls++, 5).ToArray(),
                updateCallResults: new UpdateItemResult <string>[]
            {
                null,
                null,
                null,
                null,
                UpdateItemResult.ForSuccess <string>(new CacheItem <string>("key", string.Empty), true, 100)
            },
                putCalls: Enumerable.Repeat <Action>(() => putCalls++, 5).ToArray(),
                removeCalls: Enumerable.Repeat <Action>(() => removeCalls++, 5).ToArray());

            // act
            using (cache)
            {
                string value;
                var    updateResult = cache.TryUpdate("key", updateFunc, 1, out value);

                // assert
                updateCalls.Should().Be(1, "first handle should have been invoked");
                putCalls.Should().Be(0, "evicted");
                removeCalls.Should().Be(4, "items should have been removed");
                updateResult.Should().BeTrue();
            }
        }
        public void CacheManager_Update_ExceededRetryLimit()
        {
            // arrange
            Func <string, string> updateFunc = s => s;
            int updateCalls = 0;
            int putCalls    = 0;
            int removeCalls = 0;

            var cache = MockHandles(
                count: 5,
                updateCalls: Enumerable.Repeat <Action>(() => updateCalls++, 5).ToArray(),
                updateCallResults: new UpdateItemResult <string>[]
            {
                UpdateItemResult.ForSuccess <string>(new CacheItem <string>("key", string.Empty), true, 100),
                UpdateItemResult.ForSuccess <string>(new CacheItem <string>("key", string.Empty), true, 100),
                UpdateItemResult.ForSuccess <string>(new CacheItem <string>("key", string.Empty), true, 100),
                UpdateItemResult.ForTooManyRetries <string>(1000),
                UpdateItemResult.ForItemDidNotExist <string>(),
            },
                putCalls: Enumerable.Repeat <Action>(() => putCalls++, 5).ToArray(),
                removeCalls: Enumerable.Repeat <Action>(() => removeCalls++, 5).ToArray());

            // act
            using (cache)
            {
                string value;
                var    updateResult = cache.TryUpdate("key", updateFunc, 1, out value);

                // assert
                updateCalls.Should().Be(1, "failed because item did not exist");
                putCalls.Should().Be(0, "no put calls expected");
                removeCalls.Should().Be(4, "the key should have been removed from the other 4 handles");
                updateResult.Should().BeFalse("the update in handle 4 was not successful.");
            }
        }
        public void UpdateItemResult_ForTooManyTries()
        {
            // arrange act
            Func <UpdateItemResult <object> > act = () => UpdateItemResult.ForTooManyRetries <object>(1001);

            // assert
            act().Should().BeEquivalentTo(new { Value = default(object), UpdateState = UpdateItemResultState.TooManyRetries, NumberOfTriesNeeded = 1001, VersionConflictOccurred = true });
        }
        public void UpdateItemResult_ForDidNotExist()
        {
            // arrange act
            Func <UpdateItemResult <object> > act = () => UpdateItemResult.ForItemDidNotExist <object>();

            // assert
            act().Should().BeEquivalentTo(new { Value = default(object), UpdateState = UpdateItemResultState.ItemDidNotExist, NumberOfTriesNeeded = 1, VersionConflictOccurred = false });
        }
        public void UpdateItemResult_ForFactoryReturnsNull()
        {
            // arrange act
            Func <UpdateItemResult <object> > act = () => UpdateItemResult.ForFactoryReturnedNull <object>();

            // assert
            act().Should().BeEquivalentTo(new { Value = default(object), UpdateState = UpdateItemResultState.FactoryReturnedNull, NumberOfTriesNeeded = 1, VersionConflictOccurred = false });
        }
Esempio n. 8
0
        public void UpdateItemResult_ForSuccess()
        {
            // arrange act
            Func <UpdateItemResult <object> > act = () => UpdateItemResult.ForSuccess <object>("value", true, 1001);

            // assert
            act().ShouldBeEquivalentTo(new { Value = "value", UpdateState = UpdateItemResultState.Success, NumberOfTriesNeeded = 1001, VersionConflictOccurred = true });
        }
Esempio n. 9
0
 public UpdatePointResult(UpdateItemResult updateItemResult)
 {
     if (updateItemResult == null)
     {
         throw new ArgumentNullException("updateItemResult");
     }
     UpdateItemResult = updateItemResult;
 }
Esempio n. 10
0
#pragma warning disable SA1600
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member

        protected UpdateItemResult <TCacheValue> UpdateNoScript(string key, string region, Func <TCacheValue, TCacheValue> updateValue, int maxRetries)
        {
            var committed = false;
            var tries     = 0;
            var fullKey   = GetKey(key, region);

            return(Retry(() =>
            {
                do
                {
                    tries++;

                    var item = GetCacheItemInternal(key, region);

                    if (item == null)
                    {
                        return UpdateItemResult.ForItemDidNotExist <TCacheValue>();
                    }

                    ValidateExpirationTimeout(item);

                    var oldValue = ToRedisValue(item.Value);

                    var tran = _connection.Database.CreateTransaction();
                    tran.AddCondition(Condition.HashEqual(fullKey, HashFieldValue, oldValue));

                    // run update
                    var newValue = updateValue(item.Value);

                    // added null check, throw explicit to me more consistent. Otherwise it would throw later
                    if (newValue == null)
                    {
                        return UpdateItemResult.ForFactoryReturnedNull <TCacheValue>();
                    }

                    tran.HashSetAsync(fullKey, HashFieldValue, ToRedisValue(newValue));

                    committed = tran.Execute();

                    if (committed)
                    {
                        var newItem = item.WithValue(newValue);
                        newItem.LastAccessedUtc = DateTime.UtcNow;

                        if (newItem.ExpirationMode == ExpirationMode.Sliding && newItem.ExpirationTimeout != TimeSpan.Zero)
                        {
                            _connection.Database.KeyExpire(fullKey, newItem.ExpirationTimeout, CommandFlags.FireAndForget);
                        }

                        return UpdateItemResult.ForSuccess(newItem, tries > 1, tries);
                    }

                    Logger.LogDebug("Update of {0} {1} failed with version conflict, retrying {2}/{3}", key, region, tries, maxRetries);
                }while (committed == false && tries <= maxRetries);

                return UpdateItemResult.ForTooManyRetries <TCacheValue>(tries);
            }));
        }
Esempio n. 11
0
        /// <inheritdoc />
        public override UpdateItemResult <TCacheValue> Update(string key, string region, Func <TCacheValue, TCacheValue> updateValue, int maxRetries)
        {
            if (!_isLuaAllowed)
            {
                return(UpdateNoScript(key, region, updateValue, maxRetries));
            }

            var tries   = 0;
            var fullKey = GetKey(key, region);

            return(Retry(() =>
            {
                do
                {
                    tries++;

                    var item = GetCacheItemAndVersion(key, region, out int version);

                    if (item == null)
                    {
                        return UpdateItemResult.ForItemDidNotExist <TCacheValue>();
                    }

                    ValidateExpirationTimeout(item);

                    // run update
                    var newValue = updateValue(item.Value);

                    // added null check, throw explicit to me more consistent. Otherwise it would throw within the script exec
                    if (newValue == null)
                    {
                        return UpdateItemResult.ForFactoryReturnedNull <TCacheValue>();
                    }

                    // resetting TTL on update, too
                    var result = Eval(ScriptType.Update, fullKey, new[]
                    {
                        ToRedisValue(newValue),
                        version,
                        (int)item.ExpirationMode,
                        (long)item.ExpirationTimeout.TotalMilliseconds,
                    });

                    if (result != null && !result.IsNull)
                    {
                        // optimizing not retrieving the item again after update (could have changed already, too)
                        var newItem = item.WithValue(newValue);
                        newItem.LastAccessedUtc = DateTime.UtcNow;

                        return UpdateItemResult.ForSuccess(newItem, tries > 1, tries);
                    }

                    Logger.LogDebug("Update of {0} {1} failed with version conflict, retrying {2}/{3}", key, region, tries, maxRetries);
                }while (tries <= maxRetries);

                return UpdateItemResult.ForTooManyRetries <TCacheValue>(tries);
            }));
        }
Esempio n. 12
0
        /// <inheritdoc />
        public override UpdateItemResult <TCacheValue> Update(string key, string region, Func <TCacheValue, TCacheValue> updateValue, int maxRetries)
        {
            if (!this.isLuaAllowed)
            {
                return(this.UpdateNoScript(key, region, updateValue, maxRetries));
            }

            var tries   = 0;
            var fullKey = GetKey(key, region);

            return(this.Retry(() =>
            {
                do
                {
                    tries++;

                    ////// actually slower than using the real value field, maybe suffers if the value is larger
                    ////var version = this.Database.HashIncrement(fullKey, "version", 1L);
                    ////var oldValueAndType = this.Database.HashGet(fullKey, new StackRedis.RedisValue[] { HashFieldValue, HashFieldType });
                    ////var oldValue = oldValueAndType[0];
                    ////var valueType = oldValueAndType[1];
                    ////if (oldValue.IsNull || !oldValue.HasValue || valueType.IsNull || !valueType.HasValue)
                    ////{
                    ////    return UpdateItemResult.ForItemDidNotExist<TCacheValue>();
                    ////}
                    ////var newValue = updateValue(
                    ////    this.FromRedisValue(oldValue, valueType.ToString()));

                    var item = this.GetCacheItemInternal(key, region);

                    if (item == null)
                    {
                        return UpdateItemResult.ForItemDidNotExist <TCacheValue>();
                    }

                    var oldValue = this.ToRedisValue(item.Value);

                    // run update
                    var newValue = updateValue(item.Value);

                    var result = this.Eval(ScriptType.Update, fullKey, new[]
                    {
                        this.ToRedisValue(newValue),
                        oldValue
                    });

                    if (result != null && !result.IsNull)
                    {
                        return UpdateItemResult.ForSuccess <TCacheValue>(newValue, tries > 1, tries);
                    }

                    this.Logger.LogDebug("Update of {0} {1} failed with version conflict, retrying {2}/{3}", key, region, tries, maxRetries);
                }while (tries <= maxRetries);

                this.Logger.LogWarn("Update of {0} {1} failed with version conflict exiting because of too many retries.", key, region);
                return UpdateItemResult.ForTooManyRetries <TCacheValue>(tries);
            }));
        }
        public void UpdateItemResult_ForSuccess()
        {
            // arrange act
            var item = new CacheItem <object>("key", new object());
            Func <UpdateItemResult <object> > act = () => UpdateItemResult.ForSuccess <object>(item, true, 1001);

            // assert
            act().Should().BeEquivalentTo(new { Value = item, UpdateState = UpdateItemResultState.Success, NumberOfTriesNeeded = 1001, VersionConflictOccurred = true });
        }
Esempio n. 14
0
        public void CacheManager_Update_Success_ValidateEvict()
        {
            // arrange
            Func <string, string> updateFunc = s => s;

            int updateCalls = 0;
            int addCalls    = 0;
            int putCalls    = 0;
            int removeCalls = 0;

            var cache = MockHandles(
                count: 5,
                updateCalls: Enumerable.Repeat <Action>(() => updateCalls++, 5).ToArray(),
                updateCallResults: new UpdateItemResult <string>[]
            {
                UpdateItemResult.ForItemDidNotExist <string>(),
                UpdateItemResult.ForItemDidNotExist <string>(),
                UpdateItemResult.ForItemDidNotExist <string>(),
                UpdateItemResult.ForSuccess("some value", true, 100),
                UpdateItemResult.ForItemDidNotExist <string>()
            },
                putCalls: Enumerable.Repeat <Action>(() => putCalls++, 5).ToArray(),
                removeCalls: Enumerable.Repeat <Action>(() => removeCalls++, 5).ToArray(),
                getCallValues: new CacheItem <string>[]
            {
                null,
                null,
                null,
                new CacheItem <string>("key", "updated value"),     // have to return an item for the second one
                null
            },
                addCalls: Enumerable.Repeat <Func <bool> >(() => { addCalls++; return(true); }, 5).ToArray());

            cache.Configuration.CacheUpdateMode = CacheUpdateMode.Up;

            // the update config setting it to UpdateOtherCaches
            UpdateItemConfig updateConfig = new UpdateItemConfig(0, VersionConflictHandling.UpdateOtherCaches);

            // act
            using (cache)
            {
                string value;
                var    updateResult = cache.TryUpdate("key", updateFunc, updateConfig, out value);

                // assert
                updateCalls.Should().Be(2, "second from below did update");
                putCalls.Should().Be(0, "no puts");
                addCalls.Should().Be(1, "one below the one updating");
                removeCalls.Should().Be(3, "3 above");
                updateResult.Should().BeTrue("updated successfully.");
            }
        }
Esempio n. 15
0
        private UpdateItemResult <TCacheValue> Set(string key, string region, Func <TCacheValue, TCacheValue> updateValue, int maxRetries)
        {
            var fullyKey = GetKey(key, region);
            var tries    = 0;
            IStoreOperationResult result;

            do
            {
                tries++;
                var        getTries = 0;
                StatusCode getStatus;
                CacheItem <TCacheValue> item;
                CasResult <CacheItem <TCacheValue> > cas;
                do
                {
                    getTries++;
                    cas = this.Cache.GetWithCas <CacheItem <TCacheValue> >(fullyKey);

                    item      = cas.Result;
                    getStatus = (StatusCode)cas.StatusCode;
                }while (ShouldRetry(getStatus) && getTries <= maxRetries);

                // break operation if we cannot retrieve the object (maybe it has expired already).
                if (getStatus != StatusCode.Success || item == null)
                {
                    return(UpdateItemResult.ForItemDidNotExist <TCacheValue>());
                }

                item = item.WithValue(updateValue(item.Value));

                if (item.ExpirationMode == ExpirationMode.Absolute)
                {
                    var timeoutDate = item.ExpirationTimeout;
                    result = this.Cache.ExecuteCas(StoreMode.Set, fullyKey, item, timeoutDate, cas.Cas);
                }
                else if (item.ExpirationMode == ExpirationMode.Sliding)
                {
                    result = this.Cache.ExecuteCas(StoreMode.Set, fullyKey, item, item.ExpirationTimeout, cas.Cas);
                }
                else
                {
                    result = this.Cache.ExecuteCas(StoreMode.Set, fullyKey, item, cas.Cas);
                }

                if (result.Success)
                {
                    return(UpdateItemResult.ForSuccess <TCacheValue>(item.Value, tries > 1, tries));
                }
            }while (!result.Success && result.StatusCode.HasValue && result.StatusCode.Value == 2 && tries <= maxRetries);

            return(UpdateItemResult.ForTooManyRetries <TCacheValue>(tries));
        }
Esempio n. 16
0
 public async Task ItemUpdate(int itemId, long count)
 {
     var result = new UpdateItemResult()
     {
         Items = new List <ItemInfoCls>()
         {
             new ItemInfoCls()
             {
                 ItemId = itemId, Count = count
             }
         }
     };
     await MsgMaker.SendMessage(WSResponseMsgID.UpdateItemResult, result);
 }
Esempio n. 17
0
        /// <summary>
        /// Updates an existing key in the cache.
        /// <para>
        /// The cache manager will make sure the update will always happen on the most recent version.
        /// </para>
        /// <para>
        /// If version conflicts occur, if for example multiple cache clients try to write the same
        /// key, and during the update process, someone else changed the value for the key, the
        /// cache manager will retry the operation.
        /// </para>
        /// <para>
        /// The <paramref name="updateValue"/> function will get invoked on each retry with the most
        /// recent value which is stored in cache.
        /// </para>
        /// </summary>
        /// <param name="key">The key to update.</param>
        /// <param name="region">The cache region.</param>
        /// <param name="updateValue">The function to perform the update.</param>
        /// <param name="config">The cache configuration used to specify the update behavior.</param>
        /// <returns>The update result which is interpreted by the cache manager.</returns>
        /// <remarks>
        /// If the cache does not use a distributed cache system. Update is doing exactly the same
        /// as Get plus Put.
        /// </remarks>
        public override UpdateItemResult <TCacheValue> Update(string key, string region, Func <TCacheValue, TCacheValue> updateValue, UpdateItemConfig config)
        {
            var committed = false;
            var tries     = 0;
            var fullKey   = GetKey(key, region);

            return(this.Retry(() =>
            {
                do
                {
                    tries++;

                    var item = this.GetCacheItemInternal(key, region);

                    if (item == null)
                    {
                        return UpdateItemResult.ForItemDidNotExist <TCacheValue>();
                    }

                    var oldValue = this.ToRedisValue(item.Value);

                    var tran = this.Database.CreateTransaction();
                    tran.AddCondition(StackRedis.Condition.HashEqual(fullKey, HashFieldValue, oldValue));

                    // run update
                    var newValue = updateValue(item.Value);

                    tran.HashSetAsync(fullKey, HashFieldValue, this.ToRedisValue(newValue));

                    committed = tran.Execute();

                    if (committed)
                    {
                        return UpdateItemResult.ForSuccess <TCacheValue>(newValue, tries > 1, tries);
                    }
                    else
                    {
                        //// just for debugging one bug in the redis client
                        //// var checkItem = this.GetCacheItemInternal(key, region);
                        //// if (newValue.Equals(checkItem.Value))
                        //// {
                        ////     throw new InvalidOperationException("Updated although not committed.");
                        //// }
                    }
                }while (committed == false && tries <= config.MaxRetries);

                return UpdateItemResult.ForTooManyRetries <TCacheValue>(tries);
            }));
        }
        public void CacheManager_Update_Success_ValidateEvict()
        {
            // arrange
            Func <string, string> updateFunc = s => s;

            int updateCalls = 0;
            int addCalls    = 0;
            int putCalls    = 0;
            int removeCalls = 0;

            var cache = MockHandles(
                count: 5,
                updateCalls: Enumerable.Repeat <Action>(() => updateCalls++, 5).ToArray(),
                updateCallResults: new UpdateItemResult <string>[]
            {
                UpdateItemResult.ForItemDidNotExist <string>(),
                UpdateItemResult.ForItemDidNotExist <string>(),
                UpdateItemResult.ForItemDidNotExist <string>(),
                UpdateItemResult.ForItemDidNotExist <string>(),
                UpdateItemResult.ForSuccess(new CacheItem <string>("key", "some value"), true, 100)
            },
                putCalls: Enumerable.Repeat <Action>(() => putCalls++, 5).ToArray(),
                removeCalls: Enumerable.Repeat <Action>(() => removeCalls++, 5).ToArray(),
                getCallValues: new CacheItem <string>[]
            {
                null,
                null,
                null,
                new CacheItem <string>("key", "updated value"),     // have to return an item for the second one
                null
            },
                addCalls: Enumerable.Repeat <Func <bool> >(() => { addCalls++; return(true); }, 5).ToArray());

            // act
            using (cache)
            {
                string value;
                var    updateResult = cache.TryUpdate("key", updateFunc, 1, out value);

                // assert
                updateCalls.Should().Be(1, "first succeeds second fails");
                putCalls.Should().Be(0, "no puts");
                addCalls.Should().Be(0, "no adds");
                removeCalls.Should().Be(4, "should remove from all others");
                updateResult.Should().BeTrue("updated successfully.");
            }
        }
Esempio n. 19
0
        /// <inheritdoc />
        public override UpdateItemResult <TCacheValue> Update(string key, string region, Func <TCacheValue, TCacheValue> updateValue, int maxRetries)
        {
            if (!this.isLuaAllowed)
            {
                return(this.UpdateNoScript(key, region, updateValue, maxRetries));
            }

            var tries   = 0;
            var fullKey = GetKey(key, region);

            return(this.Retry(() =>
            {
                do
                {
                    tries++;

                    var item = this.GetCacheItemInternal(key, region);

                    if (item == null)
                    {
                        return UpdateItemResult.ForItemDidNotExist <TCacheValue>();
                    }

                    var oldValue = this.ToRedisValue(item.Value);

                    // run update
                    var newValue = updateValue(item.Value);

                    var result = this.Eval(ScriptType.Update, fullKey, new[]
                    {
                        this.ToRedisValue(newValue),
                        oldValue
                    });

                    if (result != null && !result.IsNull)
                    {
                        return UpdateItemResult.ForSuccess(newValue, tries > 1, tries);
                    }

                    this.Logger.LogDebug("Update of {0} {1} failed with version conflict, retrying {2}/{3}", key, region, tries, maxRetries);
                }while (tries <= maxRetries);

                this.Logger.LogWarn("Update of {0} {1} failed with version conflict exiting because of too many retries.", key, region);
                return UpdateItemResult.ForTooManyRetries <TCacheValue>(tries);
            }));
        }
        public void CacheManager_Events_OnUpdate <T>(T cache)
            where T : ICacheManager <object>
        {
            using (cache)
            {
                // arrange
                var data = new EventCallbackData();
                var key1 = Guid.NewGuid().ToString();
                var key2 = Guid.NewGuid().ToString();

                // all callbacks should be triggered, so result count should be 4
                cache.OnUpdate += (sender, args) => data.AddCall(args, key1, key2);
                cache.OnPut    += (sender, args) => data.AddCall(args, key1, key2); // this should not trigger
                cache.OnAdd    += (sender, args) => data.AddCall(args, key1, key2); // we should have 3times add
                cache.OnGet    += (sender, args) => data.AddCall(args, key1, key2); // this should not trigger
                cache.OnRemove += (sender, args) => data.AddCall(args, key1, key2); // this should not trigger

                // act get without region, should not return anything and should not trigger the event
                cache.Add(key1, 1, "region");
                cache.Add(key2, 1, "region2");
                cache.Add(key1, 1);

                cache.Update(key1, "region", o => ((int)o) + 1);
                cache.Update(key2, "region2", o => ((int)o) + 1);
                cache.Update(key1, o => ((int)o) + 1);

                // assert 4x Put calls x 3 event handles = 12 calls
                data.Calls.Should().Be(6, "we expect 6 hits");
                data.Keys.ShouldAllBeEquivalentTo(
                    new string[] { key1, key2, key1, key1, key2, key1 },
                    cfg => cfg.WithStrictOrdering(),
                    "we expect 3 adds and 3 updates in exact order");

                data.Regions.ShouldAllBeEquivalentTo(
                    new string[] { "region", "region2", "region", "region2", },
                    cfg => cfg.WithStrictOrdering(),
                    "we expect 4 region hits");

                data.Results.ShouldAllBeEquivalentTo(
                    Enumerable.Repeat(UpdateItemResult.ForSuccess <object>(2, false, 1), 3),
                    "we expect exactly 3 update results with the same results");
            }
        }
Esempio n. 21
0
        public void CacheManager_Update_ItemDoesNotExist()
        {
            // arrange
            Func <string, string> updateFunc = s => s;
            int updateCalls = 0;
            int putCalls    = 0;
            int removeCalls = 0;

            var cache = MockHandles(
                count: 5,
                updateCalls: Enumerable.Repeat <Action>(() => updateCalls++, 5).ToArray(),
                updateCallResults: new UpdateItemResult <string>[]
            {
                UpdateItemResult.ForItemDidNotExist <string>(),
                UpdateItemResult.ForItemDidNotExist <string>(),
                UpdateItemResult.ForItemDidNotExist <string>(),
                UpdateItemResult.ForItemDidNotExist <string>(),
                UpdateItemResult.ForItemDidNotExist <string>()
            },
                putCalls: Enumerable.Repeat <Action>(() => putCalls++, 5).ToArray(),
                removeCalls: Enumerable.Repeat <Action>(() => removeCalls++, 5).ToArray());

            cache.Configuration.CacheUpdateMode = CacheUpdateMode.Up;

            // the update config setting it to Ignore
            UpdateItemConfig updateConfig = new UpdateItemConfig(0, VersionConflictHandling.EvictItemFromOtherCaches);

            // act
            using (cache)
            {
                string value;
                var    updateResult = cache.TryUpdate("key", updateFunc, updateConfig, out value);

                // assert
                updateCalls.Should().Be(5, "should iterate through all of them");
                putCalls.Should().Be(0, "no put calls expected");
                removeCalls.Should().Be(0);
                updateResult.Should().BeFalse();
            }
        }
Esempio n. 22
0
        public void CacheManager_Update_ExceededRetryLimit()
        {
            // arrange
            Func <string, string> updateFunc = s => s;
            int updateCalls = 0;
            int putCalls    = 0;
            int removeCalls = 0;

            var cache = MockHandles(
                count: 5,
                updateCalls: Enumerable.Repeat <Action>(() => updateCalls++, 5).ToArray(),
                updateCallResults: new UpdateItemResult <string>[]
            {
                UpdateItemResult.ForSuccess <string>(string.Empty, true, 100),
                UpdateItemResult.ForSuccess <string>(string.Empty, true, 100),
                UpdateItemResult.ForSuccess <string>(string.Empty, true, 100),
                UpdateItemResult.ForTooManyRetries <string>(1000),
                UpdateItemResult.ForItemDidNotExist <string>(),
            },
                putCalls: Enumerable.Repeat <Action>(() => putCalls++, 5).ToArray(),
                removeCalls: Enumerable.Repeat <Action>(() => removeCalls++, 5).ToArray());

            cache.Configuration.CacheUpdateMode = CacheUpdateMode.Up;

            // the update config setting it to EvictItemFromOtherCaches
            UpdateItemConfig updateConfig = new UpdateItemConfig(0, VersionConflictHandling.EvictItemFromOtherCaches);

            // act
            using (cache)
            {
                string value;
                var    updateResult = cache.TryUpdate("key", updateFunc, updateConfig, out value);

                // assert
                updateCalls.Should().Be(2, "bottom to top");
                putCalls.Should().Be(0, "no put calls expected");
                removeCalls.Should().Be(4, "the key should have been removed from the other 4 handles");
                updateResult.Should().BeFalse("the update in handle 4 was not successful.");
            }
        }
Esempio n. 23
0
        public void CacheManager_Update_Validate_LowestWins()
        {
            // arrange
            Func <string, string> updateFunc = s => s;
            int updateCalls = 0;
            int putCalls    = 0;
            int removeCalls = 0;

            var cache = MockHandles(
                count: 5,
                updateCalls: Enumerable.Repeat <Action>(() => updateCalls++, 5).ToArray(),
                updateCallResults: new UpdateItemResult <string>[]
            {
                null,
                null,
                null,
                null,
                UpdateItemResult.ForSuccess <string>(string.Empty, true, 100)
            },
                putCalls: Enumerable.Repeat <Action>(() => putCalls++, 5).ToArray(),
                removeCalls: Enumerable.Repeat <Action>(() => removeCalls++, 5).ToArray());

            cache.Configuration.CacheUpdateMode = CacheUpdateMode.Up;

            // the update config setting it to Ignore: update handling should be ignore, update was success, items shoudl get evicted from others
            UpdateItemConfig updateConfig = new UpdateItemConfig(0, VersionConflictHandling.Ignore);

            // act
            using (cache)
            {
                string value;
                var    updateResult = cache.TryUpdate("key", updateFunc, updateConfig, out value);

                // assert
                updateCalls.Should().Be(1, "first handle should have been invoked");
                putCalls.Should().Be(0, "evicted");
                removeCalls.Should().Be(4, "items should have been removed");
                updateResult.Should().BeTrue();
            }
        }
Esempio n. 24
0
        /// <summary>
        /// Updates an existing key in the cache.
        /// <para>
        /// The cache manager will make sure the update will always happen on the most recent version.
        /// </para>
        /// <para>
        /// If version conflicts occur, if for example multiple cache clients try to write the same
        /// key, and during the update process, someone else changed the value for the key, the
        /// cache manager will retry the operation.
        /// </para>
        /// <para>
        /// The <paramref name="updateValue"/> function will get invoked on each retry with the most
        /// recent value which is stored in cache.
        /// </para>
        /// </summary>
        /// <param name="key">The key to update.</param>
        /// <param name="region">The cache region.</param>
        /// <param name="updateValue">The function to perform the update.</param>
        /// <param name="config">The cache configuration used to specify the update behavior.</param>
        /// <returns>The update result which is interpreted by the cache manager.</returns>
        /// <remarks>
        /// If the cache does not use a distributed cache system. Update is doing exactly the same
        /// as Get plus Put.
        /// </remarks>
        public override UpdateItemResult <TCacheValue> Update(string key, string region, Func <TCacheValue, TCacheValue> updateValue, UpdateItemConfig config)
        {
            var tries   = 0;
            var fullKey = GetKey(key, region);

            return(this.Retry(() =>
            {
                do
                {
                    tries++;

                    var item = this.GetCacheItemInternal(key, region);

                    if (item == null)
                    {
                        return UpdateItemResult.ForItemDidNotExist <TCacheValue>();
                    }

                    var oldValue = this.ToRedisValue(item.Value);

                    // run update
                    var newValue = updateValue(item.Value);

                    var result = this.Eval(ScriptType.Update, new
                    {
                        key = (StackRedis.RedisKey)fullKey,
                        valField = HashFieldValue,
                        val = this.ToRedisValue(newValue),
                        oldVal = oldValue
                    });

                    if (result != null && !result.IsNull)
                    {
                        return UpdateItemResult.ForSuccess <TCacheValue>(newValue, tries > 1, tries);
                    }
                }while (tries <= config.MaxRetries);

                return UpdateItemResult.ForTooManyRetries <TCacheValue>(tries);
            }));
        }
Esempio n. 25
0
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
        protected UpdateItemResult <TCacheValue> UpdateNoScript(string key, string region, Func <TCacheValue, TCacheValue> updateValue, int maxRetries)
        {
            var committed = false;
            var tries     = 0;
            var fullKey   = GetKey(key, region);

            return(this.Retry(() =>
            {
                do
                {
                    tries++;

                    var item = this.GetCacheItemInternal(key, region);

                    if (item == null)
                    {
                        return UpdateItemResult.ForItemDidNotExist <TCacheValue>();
                    }

                    var oldValue = this.ToRedisValue(item.Value);

                    var tran = this.connection.Database.CreateTransaction();
                    tran.AddCondition(StackRedis.Condition.HashEqual(fullKey, HashFieldValue, oldValue));

                    // run update
                    var newValue = updateValue(item.Value);

                    tran.HashSetAsync(fullKey, HashFieldValue, this.ToRedisValue(newValue));

                    committed = tran.Execute();

                    if (committed)
                    {
                        return UpdateItemResult.ForSuccess <TCacheValue>(newValue, tries > 1, tries);
                    }
                }while (committed == false && tries <= maxRetries);

                return UpdateItemResult.ForTooManyRetries <TCacheValue>(tries);
            }));
        }
Esempio n. 26
0
        public async Task <UpdateItemResult> UpdateAsync(UpdateItemModel updateItemModel)
        {
            var authResult = await authenticatorsStore.UpdateItemModelAuthenticator.AuthenticateAsync(updateItemModel);

            if (!authResult.Succeed)
            {
                return(new UpdateItemResult(authResult));
            }
            var validResult = await validatorsStore.UpdateItemModelValidator.ValidateAsync(updateItemModel);

            if (!validResult.Succeed)
            {
                return(new UpdateItemResult(validResult));
            }
            var result    = new UpdateItemResult();
            var itemModel = mapper.Map <ItemModel>(updateItemModel);
            await tagsManager.AttachTagsToItemAsync(updateItemModel.Tags, itemModel.Id);

            await collectionsManager.AttachItemToCollection(itemModel.Id, updateItemModel.CollectionId);

            await itemsCrudService.UpdateAsync(itemModel);

            return(result);
        }
        private UpdateItemResult <TCacheValue> Set(string key, string region, Func <TCacheValue, TCacheValue> updateValue, int maxRetries)
        {
            var fullyKey = GetKey(key, region);
            var tries    = 0;
            IStoreOperationResult result;

            do
            {
                tries++;
                var        getTries = 0;
                StatusCode getStatus;
                CacheItem <TCacheValue> item;
                CasResult <CacheItem <TCacheValue> > cas;
                do
                {
                    getTries++;
                    cas = this.Cache.GetWithCas <CacheItem <TCacheValue> >(fullyKey);

                    item      = cas.Result;
                    getStatus = (StatusCode)cas.StatusCode;
                }while (ShouldRetry(getStatus) && getTries <= maxRetries);

                // break operation if we cannot retrieve the object (maybe it has expired already).
                if (getStatus != StatusCode.Success || item == null || item.IsExpired)
                {
                    return(UpdateItemResult.ForItemDidNotExist <TCacheValue>());
                }

                var newValue = updateValue(item.Value);

                // added null check, throw explicit to me more consistent. Otherwise it would throw later eventually
                if (newValue == null)
                {
                    return(UpdateItemResult.ForFactoryReturnedNull <TCacheValue>());
                }

                item = item.WithValue(newValue);
                item.LastAccessedUtc = DateTime.UtcNow;

                if (item.ExpirationMode == ExpirationMode.Absolute || item.ExpirationMode == ExpirationMode.Sliding)
                {
                    if (item.ExpirationTimeout > MaximumTimeout)
                    {
                        throw new InvalidOperationException($"Timeout must not exceed {MaximumTimeout.TotalDays} days.");
                    }

                    result = this.Cache.ExecuteCas(StoreMode.Set, fullyKey, item, item.ExpirationTimeout, cas.Cas);
                }
                else
                {
                    result = this.Cache.ExecuteCas(StoreMode.Set, fullyKey, item, cas.Cas);
                }

                if (result.Success)
                {
                    return(UpdateItemResult.ForSuccess(item, tries > 1, tries));
                }
            }while (!result.Success && result.StatusCode.HasValue && result.StatusCode.Value == 2 && tries <= maxRetries);

            return(UpdateItemResult.ForTooManyRetries <TCacheValue>(tries));
        }
Esempio n. 28
0
 public override UpdateItemResult <TCacheValue> Update(string key, string region, Func <TCacheValue, TCacheValue> updateValue, int maxRetries)
 {
     return(UpdateItemResult.ForItemDidNotExist <TCacheValue>());
 }
Esempio n. 29
0
 public Task <UpdateItemResult> ExecuteAsync(UpdateItemParameters parameters, CancellationToken cancellationToken = default)
 {
     return(Task.FromResult(UpdateItemResult.Ok("Hallo world")));
 }
Esempio n. 30
0
        private UpdateItemResult <TCacheValue> Set(string key, string region, Func <TCacheValue, TCacheValue> updateValue, int maxRetries)
        {
            var fullyKey = GetKey(key, region);
            var tries    = 0;
            IStoreOperationResult result;

            do
            {
                tries++;
                var        getTries = 0;
                StatusCode getStatus;
                IGetOperationResult <CacheItem <TCacheValue> > getResult;
                CacheItem <TCacheValue> item;
                do
                {
                    getTries++;
                    getResult = Cache.ExecuteGet <CacheItem <TCacheValue> >(fullyKey);

                    item      = getResult.Value;
                    getStatus = (StatusCode)(getResult.StatusCode ?? getResult.InnerResult?.StatusCode ?? -1);
                }while (ShouldRetry(getStatus) && getTries <= maxRetries);

                // break operation if we cannot retrieve the object (maybe it has expired already).
                if (!getResult.Success || item == null)
                {
                    LogOperationResult(LogLevel.Warning, getResult, "Get item during update failed for '{0}'.", fullyKey);
                    return(UpdateItemResult.ForItemDidNotExist <TCacheValue>());
                }

                if (item.IsExpired)
                {
                    return(UpdateItemResult.ForItemDidNotExist <TCacheValue>());
                }

                var newValue = updateValue(item.Value);

                // added null check, throw explicit to me more consistent. Otherwise it would throw later eventually
                if (newValue == null)
                {
                    return(UpdateItemResult.ForFactoryReturnedNull <TCacheValue>());
                }

                item = item.WithValue(newValue);
                item.LastAccessedUtc = DateTime.UtcNow;

                if (item.ExpirationMode == ExpirationMode.Absolute || item.ExpirationMode == ExpirationMode.Sliding)
                {
                    if (item.ExpirationTimeout > _maximumTimeout)
                    {
                        throw new InvalidOperationException($"Timeout must not exceed {_maximumTimeout.TotalDays} days.");
                    }

                    result = Cache.ExecuteCas(StoreMode.Set, fullyKey, item, item.ExpirationTimeout, getResult.Cas);
                }
                else
                {
                    result = Cache.ExecuteCas(StoreMode.Set, fullyKey, item, getResult.Cas);
                }

                if (result.Success)
                {
                    return(UpdateItemResult.ForSuccess(item, tries > 1, tries));
                }
                else
                {
                    LogOperationResult(LogLevel.Warning, result, "Update failed for '{0}'.", fullyKey);
                }

                WaitRetry(tries);
            }while (tries < maxRetries);

            return(UpdateItemResult.ForTooManyRetries <TCacheValue>(tries));
        }