diff --git a/src/MarginTrading.Backend.Core/Services/ISnapshotTrackerService.cs b/src/MarginTrading.Backend.Core/Services/ISnapshotTrackerService.cs new file mode 100644 index 000000000..385c07c8d --- /dev/null +++ b/src/MarginTrading.Backend.Core/Services/ISnapshotTrackerService.cs @@ -0,0 +1,13 @@ +// Copyright (c) 2019 Lykke Corp. +// See the LICENSE file in the project root for more information. + +using System.Threading.Tasks; + +namespace MarginTrading.Backend.Core.Services +{ + public interface ISnapshotTrackerService + { + Task SetShouldRecreateSnapshot(bool value); + Task GetShouldRecreateSnapshot(); + } +} \ No newline at end of file diff --git a/src/MarginTrading.Backend.Services/Infrastructure/SnapshotService.cs b/src/MarginTrading.Backend.Services/Infrastructure/SnapshotService.cs index bc2631d68..617da1d03 100644 --- a/src/MarginTrading.Backend.Services/Infrastructure/SnapshotService.cs +++ b/src/MarginTrading.Backend.Services/Infrastructure/SnapshotService.cs @@ -37,6 +37,7 @@ public class SnapshotService : ISnapshotService private readonly ILog _log; private readonly IFinalSnapshotCalculator _finalSnapshotCalculator; private readonly ISnapshotStatusTracker _snapshotStatusTracker; + private readonly ISnapshotTrackerService _snapshotTrackerService; private readonly MarginTradingSettings _settings; private static readonly SemaphoreSlim Lock = new SemaphoreSlim(1, 1); @@ -56,6 +57,7 @@ public SnapshotService( ILog log, IFinalSnapshotCalculator finalSnapshotCalculator, ISnapshotStatusTracker snapshotStatusTracker, + ISnapshotTrackerService snapshotTrackerService, MarginTradingSettings settings) { _scheduleSettingsCacheService = scheduleSettingsCacheService; @@ -71,6 +73,7 @@ public SnapshotService( _log = log; _finalSnapshotCalculator = finalSnapshotCalculator; _snapshotStatusTracker = snapshotStatusTracker; + _snapshotTrackerService = snapshotTrackerService; _settings = settings; } @@ -204,6 +207,10 @@ await _log.WriteInfoAsync(nameof(SnapshotService), nameof(MakeTradingDataSnapsho await _tradingEngineSnapshotsRepository.AddAsync(snapshot); _snapshotStatusTracker.SnapshotCreated(); + if (status == SnapshotStatus.Draft) + { + await _snapshotTrackerService.SetShouldRecreateSnapshot(false); + } await _log.WriteInfoAsync(nameof(SnapshotService), nameof(MakeTradingDataSnapshot), $"Trading data snapshot was written to the storage. {msg}"); diff --git a/src/MarginTrading.Backend.Services/Modules/ServicesModule.cs b/src/MarginTrading.Backend.Services/Modules/ServicesModule.cs index c2a54311d..e72186d69 100644 --- a/src/MarginTrading.Backend.Services/Modules/ServicesModule.cs +++ b/src/MarginTrading.Backend.Services/Modules/ServicesModule.cs @@ -237,6 +237,10 @@ protected override void Load(ContainerBuilder builder) builder.RegisterType() .As() .SingleInstance(); + + builder.RegisterType() + .As() + .SingleInstance(); } } } diff --git a/src/MarginTrading.Backend.Services/Services/PositionHistoryHandler.cs b/src/MarginTrading.Backend.Services/Services/PositionHistoryHandler.cs index 84cdda55c..cb168e742 100644 --- a/src/MarginTrading.Backend.Services/Services/PositionHistoryHandler.cs +++ b/src/MarginTrading.Backend.Services/Services/PositionHistoryHandler.cs @@ -22,18 +22,21 @@ public class PositionHistoryHandler : IPositionHistoryHandler private readonly IRabbitMqNotifyService _rabbitMqNotifyService; private readonly IDateService _dateService; private readonly IAccountsCacheService _accountsCacheService; + private readonly ISnapshotTrackerService _snapshotTrackerService; public PositionHistoryHandler(ICqrsSender cqrsSender, IConvertService convertService, IRabbitMqNotifyService rabbitMqNotifyService, IDateService dateService, - IAccountsCacheService accountsCacheService) + IAccountsCacheService accountsCacheService, + ISnapshotTrackerService snapshotTrackerService) { _cqrsSender = cqrsSender; _convertService = convertService; _rabbitMqNotifyService = rabbitMqNotifyService; _dateService = dateService; _accountsCacheService = accountsCacheService; + _snapshotTrackerService = snapshotTrackerService; } public Task HandleOpenPosition(Position position, string additionalInfo, PositionOpenMetadata metadata) @@ -46,7 +49,7 @@ public Task HandleOpenPosition(Position position, string additionalInfo, Positio return _rabbitMqNotifyService.PositionHistory(historyEvent); } - public Task HandleClosePosition(Position position, DealContract deal, string additionalInfo) + public async Task HandleClosePosition(Position position, DealContract deal, string additionalInfo) { var positionClosedEvent = CreatePositionClosedEvent(position, deal); _cqrsSender.PublishEvent(positionClosedEvent); @@ -55,10 +58,11 @@ public Task HandleClosePosition(Position position, DealContract deal, string add PositionHistoryTypeContract.Close, deal, additionalInfo); - return _rabbitMqNotifyService.PositionHistory(historyEvent); + await _rabbitMqNotifyService.PositionHistory(historyEvent); + await _snapshotTrackerService.SetShouldRecreateSnapshot(true); } - public Task HandlePartialClosePosition(Position position, DealContract deal, string additionalInfo) + public async Task HandlePartialClosePosition(Position position, DealContract deal, string additionalInfo) { var positionClosedEvent = CreatePositionClosedEvent(position, deal); _cqrsSender.PublishEvent(positionClosedEvent); @@ -67,7 +71,8 @@ public Task HandlePartialClosePosition(Position position, DealContract deal, str PositionHistoryTypeContract.PartiallyClose, deal, additionalInfo); - return _rabbitMqNotifyService.PositionHistory(historyEvent); + await _rabbitMqNotifyService.PositionHistory(historyEvent); + await _snapshotTrackerService.SetShouldRecreateSnapshot(true); } private PositionHistoryEvent CreatePositionHistoryEvent(Position position, diff --git a/src/MarginTrading.Backend.Services/Services/SnapshotTrackerService.cs b/src/MarginTrading.Backend.Services/Services/SnapshotTrackerService.cs new file mode 100644 index 000000000..ef7eb04b3 --- /dev/null +++ b/src/MarginTrading.Backend.Services/Services/SnapshotTrackerService.cs @@ -0,0 +1,45 @@ +// Copyright (c) 2019 Lykke Corp. +// See the LICENSE file in the project root for more information. + +using System; +using System.Threading.Tasks; +using MarginTrading.Backend.Core.Services; +using Microsoft.Extensions.Logging; +using Polly; +using Polly.Retry; +using StackExchange.Redis; + +namespace MarginTrading.Backend.Services.Services +{ + public class SnapshotTrackerService : ISnapshotTrackerService + { + private readonly IDatabase _database; + private readonly AsyncRetryPolicy _retryPolicy; + + private const string RedisKey = "core:snapshot:should-recreate"; + + public SnapshotTrackerService(IConnectionMultiplexer redis, ILogger logger) + { + _database = redis.GetDatabase(); + _retryPolicy = Policy + .Handle() + .WaitAndRetryAsync(3, + x => TimeSpan.FromMilliseconds(x * 1000), + (exception, span) => logger.LogWarning("Exception: {Message}", exception?.Message)); + } + + public async Task SetShouldRecreateSnapshot(bool value) + { + await _retryPolicy.ExecuteAsync(() => _database + .StringSetAsync(RedisKey, value.ToString(), when: When.Always)); + } + + public async Task GetShouldRecreateSnapshot() + { + var value = await _retryPolicy.ExecuteAsync(() => + _database.StringGetAsync(RedisKey)); + + return value == bool.TrueString; + } + } +} \ No newline at end of file diff --git a/src/MarginTrading.Backend.Services/Workflow/EodCommandsHandler.cs b/src/MarginTrading.Backend.Services/Workflow/EodCommandsHandler.cs index ccf280f97..29990b8bf 100644 --- a/src/MarginTrading.Backend.Services/Workflow/EodCommandsHandler.cs +++ b/src/MarginTrading.Backend.Services/Workflow/EodCommandsHandler.cs @@ -13,9 +13,10 @@ using JetBrains.Annotations; using Lykke.Cqrs; using MarginTrading.Backend.Contracts.Prices; +using MarginTrading.Backend.Core.Repositories; using MarginTrading.Backend.Core.Services; +using MarginTrading.Backend.Core.Snapshots; using MarginTrading.Common.Services; -using Microsoft.Extensions.DependencyInjection; namespace MarginTrading.Backend.Services.Workflow { @@ -26,6 +27,8 @@ public class EodCommandsHandler private readonly ISnapshotService _snapshotService; private readonly IDateService _dateService; private readonly IDraftSnapshotKeeperFactory _draftSnapshotKeeperFactory; + private readonly IIdentityGenerator _identityGenerator; + private readonly ISnapshotTrackerService _snapshotTrackerService; private readonly ILog _log; public EodCommandsHandler( @@ -33,12 +36,16 @@ public EodCommandsHandler( ISnapshotService snapshotService, IDateService dateService, IDraftSnapshotKeeperFactory draftSnapshotKeeperFactory, + IIdentityGenerator identityGenerator, + ISnapshotTrackerService snapshotTrackerService, ILog log) { _quotesApi = quotesApi; _snapshotService = snapshotService; _dateService = dateService; _draftSnapshotKeeperFactory = draftSnapshotKeeperFactory; + _identityGenerator = identityGenerator; + _snapshotTrackerService = snapshotTrackerService; _log = log; } @@ -55,6 +62,15 @@ private async Task Handle(CreateSnapshotCommand command, IEventPublisher publish throw new Exception($"Could not receive quotes from BookKeeper: {quotes.ErrorCode.ToString()}"); } + var shouldRecreateSnapshot = await _snapshotTrackerService.GetShouldRecreateSnapshot(); + + if (shouldRecreateSnapshot && !command.IsMissing) + { + await _snapshotService.MakeTradingDataSnapshot(command.TradingDay, + _identityGenerator.GenerateGuid(), + SnapshotStatus.Draft); + } + var draftSnapshotKeeper = _draftSnapshotKeeperFactory.Create(command.TradingDay); await _snapshotService.MakeTradingDataSnapshotFromDraft(command.OperationId, diff --git a/tests/MarginTradingTests/BaseTests.cs b/tests/MarginTradingTests/BaseTests.cs index 806db0fb1..b14ca4641 100644 --- a/tests/MarginTradingTests/BaseTests.cs +++ b/tests/MarginTradingTests/BaseTests.cs @@ -29,6 +29,7 @@ using MarginTrading.AssetService.Contracts.ClientProfileSettings; using MarginTrading.AssetService.Contracts.Scheduling; using MarginTrading.Backend.Contracts.Events; +using MarginTrading.Backend.Core.Services; using MarginTrading.Backend.Services.AssetPairs; using MarginTrading.Backend.Services.Quotes; using MarginTradingTests.Modules; @@ -191,6 +192,10 @@ private void RegisterDependenciesCore(bool mockEvents = false) builder.RegisterInstance(exchangeConnector).As(); builder.RegisterInstance(Mock.Of()).As(); builder.RegisterInstance(Mock.Of()).As(); + + builder.RegisterInstance(Mock.Of()) + .As() + .SingleInstance(); builder.RegisterBuildCallback(c => {