Skip to content

Commit 2b27236

Browse files
authored
Merge pull request #157 from safetree/feature/echofool/fix_get-async_return_with_cas
fix GetAsync's return IGetOperation with correct Cas value
2 parents 439f4a1 + 488549f commit 2b27236

7 files changed

+153
-9
lines changed

Diff for: Enyim.Caching.Tests/MemcachedClientCasTests.cs

+69-5
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
using Enyim.Caching.Memcached;
2+
using Newtonsoft.Json.Linq;
23
using System;
3-
using System.Collections.Generic;
4-
using System.Linq;
5-
using System.Text;
4+
using System.Threading.Tasks;
65
using Xunit;
76

87
namespace Enyim.Caching.Tests
98
{
109

11-
public class MemcachedClientCasTests : MemcachedClientTestsBase
10+
public class MemcachedClientCasTests : MemcachedClientTestsBase
1211
{
1312

1413
[Fact]
@@ -35,7 +34,72 @@ public void When_Storing_Item_With_Invalid_Cas_Result_Is_Not_Successful()
3534
StoreAssertFail(casResult);
3635
}
3736

38-
}
37+
38+
[Fact]
39+
public async Task When_Storing_Item_With_Valid_Cas_Result_Is_Successful_Async()
40+
{
41+
var key = GetUniqueKey("cas");
42+
var value = GetRandomString();
43+
var comment = new Comment
44+
{
45+
Author = key,
46+
Text = value
47+
};
48+
49+
var storeResult = Store(StoreMode.Add, key, comment);
50+
StoreAssertPass(storeResult);
51+
52+
var casResult1 = await _client.GetAsync(key);
53+
GetAssertPass(casResult1, comment);
54+
55+
var casResult2 = await _client.GetAsync<Comment>(key);
56+
GetAssertPass(casResult2, comment);
57+
}
58+
59+
/// <summary>
60+
/// comment
61+
/// because <see cref="IMemcachedClient"/> use <see cref="Newtonsoft"/> as default serialization tool,
62+
/// so <see cref="IMemcachedClient.GetAsync(string)"/> will return <see cref="IGetOperation"/> with <see cref="JObject"/> as <see cref="IGetOperation.Result"/>'s type.
63+
/// </summary>
64+
public class Comment : IEquatable<Comment>, IEquatable<JObject>
65+
{
66+
public string Author { get; set; }
67+
68+
public string Text { get; set; }
69+
70+
public bool Equals(Comment other)
71+
{
72+
return other != null && other.Author == Author && other.Text == Text;
73+
}
74+
75+
public bool Equals(JObject other)
76+
{
77+
return Equals(other?.ToObject<Comment>());
78+
}
79+
80+
public override bool Equals(object obj)
81+
{
82+
if (obj == null)
83+
{
84+
return false;
85+
}
86+
if (obj is Comment comment)
87+
{
88+
return Equals(comment);
89+
}
90+
if (obj is JObject jObject)
91+
{
92+
return Equals(jObject);
93+
}
94+
return false;
95+
}
96+
97+
public override int GetHashCode()
98+
{
99+
return HashCode.Combine(Author, Text);
100+
}
101+
}
102+
}
39103
}
40104

41105
#region [ License information ]

Diff for: Enyim.Caching.Tests/MemcachedClientMutateTests.cs

+6-2
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,16 @@ public async Task When_Touch_Item_Result_Is_Successful()
3636
{
3737
var key = GetUniqueKey("touch");
3838
await _client.AddAsync(key, "value", 1);
39-
Assert.True((await _client.GetAsync<string>(key)).Success);
39+
var operationResult = await _client.GetAsync<string>(key);
40+
Assert.True(operationResult.Success);
41+
Assert.NotEqual(0UL, operationResult.Cas);
4042
var result = await _client.TouchAsync(key, TimeSpan.FromSeconds(60));
4143
await Task.Delay(1010);
4244
Assert.True(result.Success, "Success was false");
4345
Assert.True((result.StatusCode ?? 0) == 0, "StatusCode was not null or 0");
44-
Assert.True((await _client.GetAsync<string>(key)).Success);
46+
operationResult = await _client.GetAsync<string>(key);
47+
Assert.True(operationResult.Success);
48+
Assert.NotEqual(0UL, operationResult.Cas);
4549
}
4650
}
4751
}

Diff for: Enyim.Caching.Tests/MemcachedClientTestsBase.cs

+8
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,14 @@ protected void GetAssertPass(IGetOperationResult result, object expectedValue)
105105
Assert.Equal(expectedValue, result.Value);
106106
}
107107

108+
protected void GetAssertPass<T>(IGetOperationResult<T> result, T expectedValue)
109+
{
110+
Assert.True(result.Success, "Success was false");
111+
Assert.True(result.Cas > 0, "Cas value was 0");
112+
Assert.True((result.StatusCode ?? 0) == 0, "StatusCode was neither 0 nor null");
113+
Assert.Equal(expectedValue, result.Value);
114+
}
115+
108116
protected void GetAssertFail(IGetOperationResult result)
109117
{
110118
Assert.False(result.Success, "Success was true");

Diff for: Enyim.Caching/IMemcachedClient.cs

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ public interface IMemcachedClient : IDisposable
1717
bool Replace(string key, object value, int cacheSeconds);
1818
Task<bool> ReplaceAsync(string key, object value, int cacheSeconds);
1919

20+
Task<IGetOperationResult> GetAsync(string key);
2021
Task<IGetOperationResult<T>> GetAsync<T>(string key);
2122
Task<T> GetValueAsync<T>(string key);
2223
Task<T> GetValueOrCreateAsync<T>(string key, int cacheSeconds, Func<Task<T>> generator);

Diff for: Enyim.Caching/MemcachedClient.cs

+57
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,25 @@ public IGetOperationResult<T> PerformGet<T>(string key)
160160
}
161161
}
162162

163+
private bool CreateGetCommand(string key, out IGetOperationResult result, out IMemcachedNode node, out IGetOperation command)
164+
{
165+
result = new DefaultGetOperationResultFactory().Create();
166+
var hashedKey = this.keyTransformer.Transform(key);
167+
168+
node = this.pool.Locate(hashedKey);
169+
if (node == null)
170+
{
171+
var errorMessage = $"Unable to locate node with \"{key}\" key";
172+
_logger.LogError(errorMessage);
173+
result.Fail(errorMessage);
174+
command = null;
175+
return false;
176+
}
177+
178+
command = this.pool.OperationFactory.Get(hashedKey);
179+
return true;
180+
}
181+
163182
private bool CreateGetCommand<T>(string key, out IGetOperationResult<T> result, out IMemcachedNode node, out IGetOperation command)
164183
{
165184
result = new DefaultGetOperationResultFactory<T>().Create();
@@ -179,11 +198,28 @@ private bool CreateGetCommand<T>(string key, out IGetOperationResult<T> result,
179198
return true;
180199
}
181200

201+
private IGetOperationResult BuildGetCommandResult(IGetOperationResult result, IGetOperation command, IOperationResult commandResult)
202+
{
203+
if (commandResult.Success)
204+
{
205+
result.Value = transcoder.Deserialize(command.Result);
206+
result.Cas = command.CasValue;
207+
result.Pass();
208+
}
209+
else
210+
{
211+
commandResult.Combine(result);
212+
}
213+
214+
return result;
215+
}
216+
182217
private IGetOperationResult<T> BuildGetCommandResult<T>(IGetOperationResult<T> result, IGetOperation command, IOperationResult commandResult)
183218
{
184219
if (commandResult.Success)
185220
{
186221
result.Value = transcoder.Deserialize<T>(command.Result);
222+
result.Cas = command.CasValue;
187223
result.Pass();
188224
}
189225
else
@@ -194,6 +230,27 @@ private IGetOperationResult<T> BuildGetCommandResult<T>(IGetOperationResult<T> r
194230
return result;
195231
}
196232

233+
public async Task<IGetOperationResult> GetAsync(string key)
234+
{
235+
if (!CreateGetCommand(key, out var result, out var node, out var command))
236+
{
237+
_logger.LogInformation($"Failed to CreateGetCommand for '{key}' key");
238+
return result;
239+
}
240+
241+
try
242+
{
243+
var commandResult = await node.ExecuteAsync(command);
244+
return BuildGetCommandResult(result, command, commandResult);
245+
}
246+
catch (Exception ex)
247+
{
248+
_logger.LogError(0, ex, $"{nameof(GetAsync)}(\"{key}\")");
249+
result.Fail(ex.Message, ex);
250+
return result;
251+
}
252+
}
253+
197254
public async Task<IGetOperationResult<T>> GetAsync<T>(string key)
198255
{
199256
if (!CreateGetCommand<T>(key, out var result, out var node, out var command))

Diff for: Enyim.Caching/MemcachedClientT.cs

+5
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,11 @@ public IDictionary<string, T1> Get<T1>(IEnumerable<string> keys)
120120
return _memcachedClient.Get<T1>(keys);
121121
}
122122

123+
public Task<IGetOperationResult> GetAsync(string key)
124+
{
125+
return _memcachedClient.GetAsync(key);
126+
}
127+
123128
public Task<IGetOperationResult<T1>> GetAsync<T1>(string key)
124129
{
125130
return _memcachedClient.GetAsync<T1>(key);

Diff for: Enyim.Caching/NullMemcachedClient.cs

+7-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
using System;
22
using System.Collections.Generic;
3-
using System.Linq;
43
using System.Net;
5-
using System.Text;
64
using System.Threading.Tasks;
75
using Enyim.Caching.Memcached;
86
using Enyim.Caching.Memcached.Results;
@@ -104,6 +102,13 @@ public T Get<T>(string key)
104102
return default(T);
105103
}
106104

105+
public Task<IGetOperationResult> GetAsync(string key)
106+
{
107+
var result = new DefaultGetOperationResultFactory().Create();
108+
result.Success = false;
109+
return Task.FromResult(result);
110+
}
111+
107112
public async Task<IGetOperationResult<T>> GetAsync<T>(string key)
108113
{
109114
var result = new DefaultGetOperationResultFactory<T>().Create();

0 commit comments

Comments
 (0)