Skip to content

Commit e88bd70

Browse files
committed
Cleaned up the code.
Added more information in the README.md
1 parent 5bef14f commit e88bd70

File tree

3 files changed

+45
-40
lines changed

3 files changed

+45
-40
lines changed

README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,15 @@ It makes use of the TTL functionality to get remove the stale cache keys.
44
IDistributedCache is a low-level interface used by cache-related consumers, such as Polly. Microsoft also provides a set of extension methods that simplify common workflows.
55

66
## Considerations
7-
7+
- There is no optimistic locking(or locking of any kind). If this is a big problem, using a different provider(such as Redis with WATCH) might be better for your use case.
8+
- Eventual consistency is used.
89
- Deleting DynamoDB items with an expired TTL takes up to 48 hours. There is a soft invalidation in the code to check whether the item has expired or not.
910
- Sliding expiration is WIP and experimental. It works in such a way that the TTL is refreshed(by the amount of the sliding expiration) when you do a GetCacheKey and the TTL is about to expire in (SlidingExpirationDuration / 2) seconds.
1011
Example:
1112
Sliding expiration is set to 30 mins.
1213
The TTL is CurrentTime + 14 mins( < SlidingExpirationDuration / 2).
1314
Doing a GET will refresh the TTL to CurrentTime + 14 mins + 30 mins.
1415
A subsequent GET will not refresh the key until the condition is met again.
15-
- While methods are thread-safe, data race conditions are possible. It's up to the caller to manage them correctly.
1616
- Sync methods are disabled by default due to the fact that Amazon's DynamoDb client uses HttpClient internally, which lacks sync methods. There is an override that will enable them, but doing so is not recommended.
1717
- The library is still **quite raw**. Any pull requests, ideas and feedback are encouraged.
1818

src/Albelli.Extensions.Caching.DynamoDb/DynamoDbCache.cs

+36-31
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ public byte[] Get(string key)
6363
{
6464
var slidingExpiry = long.Parse(result.Item[_options.SlidingExpiryTimespanColumnName].N);
6565
var currentTime = _systemClock.UtcNow.ToUnixTimeSeconds();
66-
if (currentTtl - currentTime <= slidingExpiry / 2.0)
66+
if (currentTtl - currentTime <= slidingExpiry / 2)
6767
{
6868
await RefreshAsync(key, token).ConfigureAwait(false);
6969
}
@@ -95,34 +95,8 @@ public async Task SetAsync(string key, byte[] value, DistributedCacheEntryOption
9595
options = new DistributedCacheEntryOptions()
9696
{AbsoluteExpirationRelativeToNow = _options.DefaultDurationPolicy};
9797
}
98-
99-
var table = new Dictionary<string, AttributeValue>()
100-
{
101-
[_options.KeyColumnName] = new AttributeValue(key),
102-
[_options.ValueColumnName] = new AttributeValue() {B = new MemoryStream(value)},
103-
};
104-
var slidingExpirationTicks = options.SlidingExpiration.HasValue
105-
? Convert.ToInt64(Math.Round(options.SlidingExpiration.Value.TotalSeconds))
106-
: (long?) null;
107-
var absoluteExpirationTimestamp =
108-
GetAbsoluteExpirationUnixTimestamp(options.AbsoluteExpiration, options.AbsoluteExpirationRelativeToNow)
109-
?.ToUnixTimeSeconds();
110-
if (slidingExpirationTicks.HasValue)
111-
{
112-
table.Add(_options.SlidingExpiryTimespanColumnName,
113-
new AttributeValue() {N = slidingExpirationTicks.Value.ToString()});
114-
}
115-
116-
if (absoluteExpirationTimestamp.HasValue)
117-
{
118-
table.Add(_options.AbsoluteExpiryUnixTimestampColumnName,
119-
new AttributeValue() {N = absoluteExpirationTimestamp.Value.ToString()});
120-
}
121-
122-
table.Add(_options.TimeToLiveColumnName,
123-
new AttributeValue()
124-
{N = GetInitialTtlTimestamp(slidingExpirationTicks, absoluteExpirationTimestamp).ToString()});
125-
await _dynamoDb.PutItemAsync(_options.TableName, table, token).ConfigureAwait(false);
98+
var dynamoDbCacheEntry = BuildDynamoDbCacheEntry(key, value, options);
99+
await _dynamoDb.PutItemAsync(_options.TableName, dynamoDbCacheEntry, token).ConfigureAwait(false);
126100
}
127101

128102
public void Refresh(string key)
@@ -180,6 +154,37 @@ await _dynamoDb.DeleteItemAsync(_options.TableName,
180154
token)
181155
.ConfigureAwait(false);
182156
}
157+
158+
private Dictionary<string, AttributeValue> BuildDynamoDbCacheEntry(string key, byte[] value,
159+
DistributedCacheEntryOptions options)
160+
{
161+
var table = new Dictionary<string, AttributeValue>()
162+
{
163+
[_options.KeyColumnName] = new AttributeValue(key),
164+
[_options.ValueColumnName] = new AttributeValue() {B = new MemoryStream(value)},
165+
};
166+
var slidingExpirationTicks = options.SlidingExpiration.HasValue
167+
? Convert.ToInt64(Math.Round(options.SlidingExpiration.Value.TotalSeconds))
168+
: (long?) null;
169+
var absoluteExpirationTimestamp =
170+
GetAbsoluteExpirationUnixTimestamp(options.AbsoluteExpiration, options.AbsoluteExpirationRelativeToNow);
171+
if (slidingExpirationTicks.HasValue)
172+
{
173+
table.Add(_options.SlidingExpiryTimespanColumnName,
174+
new AttributeValue() {N = slidingExpirationTicks.Value.ToString()});
175+
}
176+
177+
if (absoluteExpirationTimestamp.HasValue)
178+
{
179+
table.Add(_options.AbsoluteExpiryUnixTimestampColumnName,
180+
new AttributeValue() {N = absoluteExpirationTimestamp.Value.ToString()});
181+
}
182+
183+
table.Add(_options.TimeToLiveColumnName,
184+
new AttributeValue()
185+
{N = GetInitialTtlTimestamp(slidingExpirationTicks, absoluteExpirationTimestamp).ToString()});
186+
return table;
187+
}
183188

184189
private long GetInitialTtlTimestamp(long? slidingExpirationTicks, long? absoluteExpirationTimestamp)
185190
{
@@ -194,7 +199,7 @@ private long GetInitialTtlTimestamp(long? slidingExpirationTicks, long? absolute
194199
}
195200
}
196201

197-
private DateTimeOffset? GetAbsoluteExpirationUnixTimestamp(DateTimeOffset? absoluteExpiration,
202+
private long? GetAbsoluteExpirationUnixTimestamp(DateTimeOffset? absoluteExpiration,
198203
TimeSpan? relativeToNowAbsoluteExpiration)
199204
{
200205
DateTimeOffset? result = null;
@@ -213,7 +218,7 @@ private long GetInitialTtlTimestamp(long? slidingExpirationTicks, long? absolute
213218
result = _systemClock.UtcNow.Add(relativeToNowAbsoluteExpiration.Value);
214219
}
215220

216-
return result;
221+
return result?.ToUnixTimeSeconds();
217222
}
218223
}
219224
}

test/Albelli.Extensions.Caching.Tests.Integration/DynamoDbCacheIntegrationTests.cs

+7-7
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ public class DynamoDbCacheIntegrationTests
2121
private readonly IDistributedCache _cache;
2222
public DynamoDbCacheIntegrationTests() => _cache = new DynamoDbCache(_options, _systemClock, _dynamoDb);
2323

24-
[Fact(Skip = "Local only.")]
24+
[Fact(Skip = "Local only")]
2525
public async Task Should_Successfully_Create_And_Retrieve_Keys()
2626
{
2727
var dummyKey = $"TestKey{Guid.NewGuid():N}";
@@ -31,28 +31,28 @@ public async Task Should_Successfully_Create_And_Retrieve_Keys()
3131
Assert.Equal(dummyValue, result);
3232
}
3333

34-
[Fact(Skip = "Local only.")]
34+
[Fact(Skip = "Local only")]
3535
public async Task Should_recalculate_sliding_expiration_properly()
3636
{
3737
var dummyKey = $"TestKey{Guid.NewGuid():N}";
3838
var dummyValue = "Hi, there!";
3939
await _cache.SetStringAsync(dummyKey, dummyValue, new DistributedCacheEntryOptions()
4040
{
4141
AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(1),
42-
SlidingExpiration = TimeSpan.FromSeconds(5)
42+
SlidingExpiration = TimeSpan.FromSeconds(10)
4343
});
4444
for (var i = 0; i <= 5; i++)
4545
{
46-
await Task.Delay(3000);
46+
await Task.Delay(7000);
4747
var result = await _cache.GetStringAsync(dummyKey);
4848
Assert.Equal(dummyValue, result);
4949
}
50-
await Task.Delay(7000);
50+
await Task.Delay(25000);
5151
var finalResult = await _cache.GetStringAsync(dummyKey);
5252
Assert.Null(finalResult);
5353
}
5454

55-
[Fact(Skip = "Local only.")]
55+
[Fact(Skip = "Local only")]
5656
public async Task Should_expire_after_five_seconds()
5757
{
5858
var dummyKey = $"TestKey{Guid.NewGuid():N}";
@@ -68,7 +68,7 @@ public async Task Should_expire_after_five_seconds()
6868
Assert.Null(finalResult);
6969
}
7070

71-
[Fact(Skip = "Local only.")]
71+
[Fact(Skip = "Local only")]
7272
public async Task Should_delete_the_key_properly()
7373
{
7474
var dummyKey = $"TestKey{Guid.NewGuid():N}";

0 commit comments

Comments
 (0)