Skip to content

Commit

Permalink
Add background cache update functionality.
Browse files Browse the repository at this point in the history
  • Loading branch information
njannink committed May 30, 2018
1 parent e30f109 commit dc44853
Show file tree
Hide file tree
Showing 8 changed files with 246 additions and 30 deletions.
46 changes: 35 additions & 11 deletions client/Lykke.Service.Assets.Client/AssetsServiceWithCache.cs
Original file line number Diff line number Diff line change
@@ -1,64 +1,88 @@
using System.Collections.Generic;
using Lykke.Service.Assets.Client.Cache;
using Lykke.Service.Assets.Client.Models;
using System;
using System.Collections.Generic;
using System.Reactive.Disposables;
using System.Threading;
using System.Threading.Tasks;
using Lykke.Service.Assets.Client.Cache;
using Lykke.Service.Assets.Client.Models;
using Common.Log;

namespace Lykke.Service.Assets.Client
{
///<inheritdoc/>
public class AssetsServiceWithCache : IAssetsServiceWithCache
{
private readonly IAssetsService _assetsService;
private readonly IDictionaryCache<Asset> _assetsCache;
private readonly IAssetsService _assetsService;
private readonly IDictionaryCache<Asset> _assetsCache;
private readonly IDictionaryCache<AssetPair> _assetPairsCache;
private readonly ILog _log;


public AssetsServiceWithCache(IAssetsService assetsService, IDictionaryCache<Asset> assetsCache, IDictionaryCache<AssetPair> assetPairsCache)
///<inheritdoc/>
public AssetsServiceWithCache(IAssetsService assetsService, IDictionaryCache<Asset> assetsCache, IDictionaryCache<AssetPair> assetPairsCache, ILog log)
{
_assetsService = assetsService;
_assetsCache = assetsCache;
_assetsService = assetsService;
_assetsCache = assetsCache;
_assetPairsCache = assetPairsCache;
_log = log;
}


///<inheritdoc/>
public async Task<IReadOnlyCollection<AssetPair>> GetAllAssetPairsAsync(CancellationToken cancellationToken = new CancellationToken())
{
await _assetPairsCache.EnsureCacheIsUpdatedAsync(() => GetUncachedAssetPairsAsync(cancellationToken));

return _assetPairsCache.GetAll();
}

public async Task<IReadOnlyCollection<Asset>> GetAllAssetsAsync(CancellationToken cancellationToken = new CancellationToken())
async Task<IReadOnlyCollection<Asset>> IAssetsServiceWithCache.GetAllAssetsAsync(CancellationToken cancellationToken)
=> await GetAllAssetsAsync(false, cancellationToken);

///<inheritdoc/>
public async Task<IReadOnlyCollection<Asset>> GetAllAssetsAsync(bool includeNonTradable, CancellationToken cancellationToken = new CancellationToken())
{
await _assetsCache.EnsureCacheIsUpdatedAsync(() => GetUncachedAssetsAsync(cancellationToken));

return _assetsCache.GetAll();
}

///<inheritdoc/>
public async Task<Asset> TryGetAssetAsync(string assetId, CancellationToken cancellationToken = new CancellationToken())
{
await _assetsCache.EnsureCacheIsUpdatedAsync(() => GetUncachedAssetsAsync(cancellationToken));

return _assetsCache.TryGet(assetId);
}

///<inheritdoc/>
public async Task<AssetPair> TryGetAssetPairAsync(string assetPairId, CancellationToken cancellationToken = new CancellationToken())
{
await _assetPairsCache.EnsureCacheIsUpdatedAsync(() => GetUncachedAssetPairsAsync(cancellationToken));

return _assetPairsCache.TryGet(assetPairId);
}

///<inheritdoc/>
public async Task UpdateAssetPairsCacheAsync(CancellationToken cancellationToken = new CancellationToken())
{
_assetPairsCache.Update(await GetUncachedAssetPairsAsync(cancellationToken));
}

///<inheritdoc/>
public async Task UpdateAssetsCacheAsync(CancellationToken cancellationToken = new CancellationToken())
{
_assetsCache.Update(await GetUncachedAssetsAsync(cancellationToken));
}

///<inheritdoc/>
public IDisposable StartAutoCacheUpdate()
{
return new CompositeDisposable
{
_assetPairsCache.StartAutoUpdate(nameof(AssetsServiceWithCache), _log, () => GetUncachedAssetPairsAsync(new CancellationToken())),
_assetsCache.StartAutoUpdate(nameof(AssetsServiceWithCache), _log, () => GetUncachedAssetsAsync(new CancellationToken()))
};
}

private async Task<IEnumerable<Asset>> GetUncachedAssetsAsync(CancellationToken cancellationToken)
{
return await _assetsService.AssetGetAllAsync(false, cancellationToken);
Expand Down
61 changes: 52 additions & 9 deletions client/Lykke.Service.Assets.Client/Cache/DictionaryCache.cs
Original file line number Diff line number Diff line change
@@ -1,52 +1,95 @@
using System;
using Common;
using Common.Log;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Disposables;
using System.Threading;
using System.Threading.Tasks;

namespace Lykke.Service.Assets.Client.Cache
{
/// <inheritdoc />
public class DictionaryCache<T> : IDictionaryCache<T>
where T : ICacheItem
{
private readonly IDateTimeProvider _dateTimeProvider;
private readonly TimeSpan _cacheExpirationPeriod;
private readonly TimeSpan _cacheExpirationPeriod;

private Dictionary<string, T> _items;
private DateTime _cacheExpirationMoment;

private DateTime _cacheExpirationMoment;
private bool _inAutoUpdate;

/// <inheritdoc />
public DictionaryCache(IDateTimeProvider dateTimeProvider, TimeSpan cacheExpirationPeriod)
{
_items = new Dictionary<string, T>();
_items = new Dictionary<string, T>();
_cacheExpirationMoment = DateTime.MinValue;
_dateTimeProvider = dateTimeProvider;
_dateTimeProvider = dateTimeProvider;
_cacheExpirationPeriod = cacheExpirationPeriod;
}

/// <inheritdoc />
public IDisposable StartAutoUpdate(string componentName, ILog log, Func<Task<IEnumerable<T>>> getAllAsync)
{
if (_inAutoUpdate)
{
throw new InvalidOperationException("Dictionary is already in auto update mode.");
}

_inAutoUpdate = true;
async Task UpdateCache(ITimerTrigger trigger, TimerTriggeredHandlerArgs args, CancellationToken token)
{
await Update(getAllAsync);
}

var timer = new TimerTrigger(componentName, _cacheExpirationPeriod, log, UpdateCache);
timer.Start();

return Disposable.Create(() =>
{
_inAutoUpdate = false;
timer.Dispose();
});
}

/// <inheritdoc />
public async Task EnsureCacheIsUpdatedAsync(Func<Task<IEnumerable<T>>> getAllItemsAsync)
{
if (_cacheExpirationMoment < _dateTimeProvider.UtcNow)
if (_inAutoUpdate)
{
var items = await getAllItemsAsync();
return;
}

Update(items);
if (_cacheExpirationMoment < _dateTimeProvider.UtcNow)
{
await Update(getAllItemsAsync);
}
}

private async Task Update(Func<Task<IEnumerable<T>>> getAllItemsAsync)
{
var items = await getAllItemsAsync();
Update(items);
}

/// <inheritdoc />
public void Update(IEnumerable<T> items)
{
_items = items.ToDictionary(p => p.Id, p => p);

_cacheExpirationMoment = _dateTimeProvider.UtcNow + _cacheExpirationPeriod;
}

/// <inheritdoc />
public T TryGet(string id)
{
_items.TryGetValue(id, out var pair);

return pair;
}

/// <inheritdoc />
public IReadOnlyCollection<T> GetAll()
{
return _items.Values;
Expand Down
21 changes: 21 additions & 0 deletions client/Lykke.Service.Assets.Client/Cache/IDictionaryCache.cs
Original file line number Diff line number Diff line change
@@ -1,18 +1,39 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Common.Log;

namespace Lykke.Service.Assets.Client.Cache
{
/// <summary>
/// Simple in-memory client side cache.
/// </summary>
public interface IDictionaryCache<T>
where T : ICacheItem
{
/// <summary>
/// Starts an automatic updater that keeps the cache updated on a background thread.
/// </summary>
IDisposable StartAutoUpdate(string componentName, ILog log, Func<Task<IEnumerable<T>>> getAllAsync);

/// <summary>
/// Update the cache when cache has expired.
/// </summary>
Task EnsureCacheIsUpdatedAsync(Func<Task<IEnumerable<T>>> getAllAsync);

/// <summary>
/// Update the cache with given data.
/// </summary>
void Update(IEnumerable<T> items);

/// <summary>
/// Try to get cached item with given id.
/// </summary>
T TryGet(string id);

/// <summary>
/// Get all cached items.
/// </summary>
IReadOnlyCollection<T> GetAll();
}
}
34 changes: 31 additions & 3 deletions client/Lykke.Service.Assets.Client/IAssetsServiceWithCache.cs
Original file line number Diff line number Diff line change
@@ -1,28 +1,56 @@
using System.Collections.Generic;
using Lykke.Service.Assets.Client.Models;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Lykke.Service.Assets.Client.Models;

namespace Lykke.Service.Assets.Client
{
/// <summary>
/// Client side in-memory cached version of the <see cref="IAssetsService"/>.
/// </summary>
public interface IAssetsServiceWithCache
{
/// <summary>
/// Get all asset-pairs.
/// </summary>
Task<IReadOnlyCollection<AssetPair>> GetAllAssetPairsAsync(CancellationToken cancellationToken = new CancellationToken());

/// <summary>
/// Get all assets
/// </summary>
[Obsolete("Use GetAllAssetsAsync(bool) instead")]
Task<IReadOnlyCollection<Asset>> GetAllAssetsAsync(CancellationToken cancellationToken = new CancellationToken());

/// <summary>
/// Get all assets
/// </summary>
Task<IReadOnlyCollection<Asset>> GetAllAssetsAsync(bool includeNonTradable, CancellationToken cancellationToken = new CancellationToken());

/// <summary>
/// Try to find an asset with given id.
/// </summary>
Task<Asset> TryGetAssetAsync(string assetId, CancellationToken cancellationToken = new CancellationToken());

/// <summary>
/// Try to find an asset-pair with given id.
/// </summary>
Task<AssetPair> TryGetAssetPairAsync(string assetPairId, CancellationToken cancellationToken = new CancellationToken());

/// <summary>
/// Forcibly updates client-side asset pairs cache
/// Forcibly updates client-side asset-pairs cache
/// </summary>
Task UpdateAssetPairsCacheAsync(CancellationToken cancellationToken = new CancellationToken());

/// <summary>
/// Forcibly updates client-side assets cache
/// </summary>
Task UpdateAssetsCacheAsync(CancellationToken cancellationToken = new CancellationToken());

/// <summary>
/// Starts an automatic update process that will keep the caches updated the background.
/// </summary>
/// <returns>the update process, when disposed the auto update will stop</returns>
IDisposable StartAutoCacheUpdate();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,11 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Lykke.Common" Version="6.8.2" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="2.0.0" />
<PackageReference Include="Microsoft.Rest.ClientRuntime" Version="2.3.10" />
<PackageReference Include="Newtonsoft.Json" Version="11.0.1" />
<PackageReference Include="Microsoft.Rest.ClientRuntime" Version="2.3.11" />
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
<PackageReference Include="System.Reactive.Linq" Version="3.1.1" />
</ItemGroup>

</Project>
19 changes: 14 additions & 5 deletions client/Lykke.Service.Assets.Client/ServiceCollectionExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,24 +1,33 @@
using Lykke.Service.Assets.Client.Cache;
using Common.Log;
using Lykke.Service.Assets.Client.Cache;
using Lykke.Service.Assets.Client.Models;
using Microsoft.Extensions.DependencyInjection;

namespace Lykke.Service.Assets.Client
{
public static class ServiceCollectionExtensions
{
public static void RegisterAssetsClient(this IServiceCollection services, AssetServiceSettings settings)
public static void RegisterAssetsClient(this IServiceCollection services, AssetServiceSettings settings,
ILog log)
{
services
.AddSingleton<IAssetsService>(x => new AssetsService(settings.BaseUri, settings.Handlers));

services
.AddTransient<IAssetsServiceWithCache, AssetsServiceWithCache>();
.AddSingleton<IDictionaryCache<Asset>>(x =>
new DictionaryCache<Asset>(new DateTimeProvider(), settings.AssetsCacheExpirationPeriod));

services
.AddSingleton<IDictionaryCache<Asset>>(x => new DictionaryCache<Asset>(new DateTimeProvider(), settings.AssetsCacheExpirationPeriod));
.AddSingleton<IDictionaryCache<AssetPair>>(x =>
new DictionaryCache<AssetPair>(new DateTimeProvider(), settings.AssetPairsCacheExpirationPeriod));

services
.AddSingleton<IDictionaryCache<AssetPair>>(x => new DictionaryCache<AssetPair>(new DateTimeProvider(), settings.AssetPairsCacheExpirationPeriod));
.AddSingleton<IAssetsServiceWithCache>(x => new AssetsServiceWithCache(
x.GetService<IAssetsService>(),
x.GetService<IDictionaryCache<Asset>>(),
x.GetService<IDictionaryCache<AssetPair>>(),
log
));
}
}
}
Loading

0 comments on commit dc44853

Please sign in to comment.