-
-
Notifications
You must be signed in to change notification settings - Fork 31
Improves Redis cache list retrieval performance #116
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -246,6 +246,7 @@ private CacheValue<T> RedisValueToCacheValue<T>(RedisValue redisValue) | |
| } | ||
| } | ||
|
|
||
|
|
||
| public async Task<IDictionary<string, CacheValue<T>>> GetAllAsync<T>(IEnumerable<string> keys) | ||
| { | ||
| if (keys is null) | ||
|
|
@@ -256,6 +257,7 @@ public async Task<IDictionary<string, CacheValue<T>>> GetAllAsync<T>(IEnumerable | |
| if (redisKeys.Length == 0) | ||
| return result; | ||
|
|
||
| // parallelize? | ||
| if (_options.ConnectionMultiplexer.IsCluster()) | ||
| { | ||
| foreach (var hashSlotGroup in redisKeys.GroupBy(k => _options.ConnectionMultiplexer.HashSlot(k))) | ||
|
|
@@ -284,19 +286,25 @@ public async Task<CacheValue<ICollection<T>>> GetListAsync<T>(string key, int? p | |
| if (page is < 1) | ||
| throw new ArgumentOutOfRangeException(nameof(page), "Page cannot be less than 1"); | ||
|
|
||
| await RemoveExpiredListValuesAsync<T>(key, typeof(T) == typeof(string)).AnyContext(); | ||
|
|
||
| if (!page.HasValue) | ||
| try | ||
| { | ||
| var set = await Database.SortedSetRangeByScoreAsync(key, flags: _options.ReadMode).AnyContext(); | ||
| return RedisValuesToCacheValue<T>(set); | ||
| if (!page.HasValue) | ||
| { | ||
| var set = await Database.SortedSetRangeByScoreAsync(key, _timeProvider.GetUtcNow().ToUnixTimeMilliseconds() + 1, flags: _options.ReadMode).AnyContext(); | ||
|
||
| return RedisValuesToCacheValue<T>(set); | ||
| } | ||
| else | ||
| { | ||
| long skip = (page.Value - 1) * pageSize; | ||
| var set = await Database.SortedSetRangeByScoreAsync(key, _timeProvider.GetUtcNow().ToUnixTimeMilliseconds() + 1, skip: skip, take: pageSize, flags: _options.ReadMode).AnyContext(); | ||
|
||
| return RedisValuesToCacheValue<T>(set); | ||
| } | ||
| } | ||
| else | ||
| catch (RedisServerException ex) when (ex.Message.StartsWith("WRONGTYPE")) | ||
| { | ||
| long start = (page.Value - 1) * pageSize; | ||
| long end = start + pageSize - 1; | ||
| var set = await Database.SortedSetRangeByRankAsync(key, start, end, flags: _options.ReadMode).AnyContext(); | ||
| return RedisValuesToCacheValue<T>(set); | ||
| _logger.LogInformation(ex, "Migrating legacy set to sorted set for key: {Key}", key); | ||
| await MigrateLegacySetToSortedSetForKeyAsync<T>(key, typeof(T) == typeof(string)).AnyContext(); | ||
| return await GetListAsync<T>(key, page, pageSize).AnyContext(); | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -375,7 +383,10 @@ private async Task SetListExpirationAsync(string key) | |
| { | ||
| var items = await Database.SortedSetRangeByRankWithScoresAsync(key, 0, 0, order: Order.Descending).AnyContext(); | ||
| if (items.Length == 0) | ||
| { | ||
| _logger.LogTrace("Sorted set is empty for key: {Key}, expiration will not be set", key); | ||
| return; | ||
| } | ||
|
|
||
| long highestExpirationInMs = (long)items.Single().Score; | ||
| if (highestExpirationInMs > MaxUnixEpochMilliseconds) | ||
|
|
@@ -403,32 +414,35 @@ private async Task RemoveExpiredListValuesAsync<T>(string key, bool isStringValu | |
| catch (RedisServerException ex) when (ex.Message.StartsWith("WRONGTYPE")) | ||
| { | ||
| _logger.LogInformation(ex, "Migrating legacy set to sorted set for key: {Key}", key); | ||
| await MigrateLegacySetToSortedSetForKeyAsync<T>(key, isStringValues).AnyContext(); | ||
| } | ||
| } | ||
|
|
||
| // convert legacy set to sorted set | ||
| var oldItems = await Database.SetMembersAsync(key).AnyContext(); | ||
| await Database.KeyDeleteAsync(key).AnyContext(); | ||
|
|
||
| var currentKeyExpiresIn = await GetExpirationAsync(key).AnyContext(); | ||
| if (isStringValues) | ||
| { | ||
| var oldItemValues = new List<string>(oldItems.Length); | ||
| foreach (string oldItem in RedisValuesToCacheValue<string>(oldItems).Value) | ||
| oldItemValues.Add(oldItem); | ||
|
|
||
| await ListAddAsync(key, oldItemValues, currentKeyExpiresIn).AnyContext(); | ||
| } | ||
| else | ||
| { | ||
| var oldItemValues = new List<T>(oldItems.Length); | ||
| foreach (var oldItem in RedisValuesToCacheValue<T>(oldItems).Value) | ||
| oldItemValues.Add(oldItem); | ||
| private async Task MigrateLegacySetToSortedSetForKeyAsync<T>(string key, bool isStringValues) | ||
| { | ||
| // convert legacy set to sorted set | ||
| var oldItems = await Database.SetMembersAsync(key).AnyContext(); | ||
| var currentKeyExpiresIn = await GetExpirationAsync(key).AnyContext(); | ||
| await Database.KeyDeleteAsync(key).AnyContext(); | ||
| if (isStringValues) | ||
| { | ||
| var oldItemValues = new List<string>(oldItems.Length); | ||
| foreach (string oldItem in RedisValuesToCacheValue<string>(oldItems).Value) | ||
| oldItemValues.Add(oldItem); | ||
|
|
||
| await ListAddAsync(key, oldItemValues, currentKeyExpiresIn).AnyContext(); | ||
| } | ||
| await ListAddAsync(key, oldItemValues, currentKeyExpiresIn).AnyContext(); | ||
| } | ||
| else | ||
| { | ||
| var oldItemValues = new List<T>(oldItems.Length); | ||
| foreach (var oldItem in RedisValuesToCacheValue<T>(oldItems).Value) | ||
| oldItemValues.Add(oldItem); | ||
|
|
||
| if (currentKeyExpiresIn.HasValue) | ||
| await Database.KeyExpireAsync(key, (DateTime?)null).AnyContext(); | ||
| await ListAddAsync(key, oldItemValues, currentKeyExpiresIn).AnyContext(); | ||
| } | ||
|
|
||
| if (currentKeyExpiresIn.HasValue) | ||
| await Database.KeyExpireAsync(key, (DateTime?)null).AnyContext(); | ||
| } | ||
|
|
||
| public Task<bool> SetAsync<T>(string key, T value, TimeSpan? expiresIn = null) | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.