-
Notifications
You must be signed in to change notification settings - Fork 7
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
feat: Support persisting SQLite DB, and data refresher #233
Open
lukehesluke
wants to merge
24
commits into
master
Choose a base branch
from
feature/ref-impl-db-2
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 23 commits
Commits
Show all changes
24 commits
Select commit
Hold shift + click to select a range
0eb9c6b
database persists. Next step - refresher
lukehesluke 9dca035
done the DataRefresher. Next step: test in anger
lukehesluke 57839ea
confirm pls
lukehesluke 021847b
match Retention Period policy
lukehesluke 117c1d9
Improved logs and confirmed FakeDataRefresherService to work
lukehesluke c49b149
some cleanup
lukehesluke 0a02f4a
remove web-app-package
lukehesluke 5fd44fd
fix .NET SDK version with global.json
lukehesluke eceb398
fix an RPDE issue - soft-deletes were not updating their modifieds
lukehesluke a3b193a
able to turn off flags easier with env vars
lukehesluke a6e2aac
/init-wait/data-refresher
lukehesluke af3ac48
remove CI scripts which are now in feature/persistent-db-ci
lukehesluke fa828f9
install libssl?
lukehesluke 118424e
Revert "install libssl?"
lukehesluke d73724a
remove global.json?
lukehesluke cfd6e03
TESTING CI
lukehesluke 79a642d
some fixes
lukehesluke 0be04d4
ubuntu-22.04?
lukehesluke 1a33b81
upgrade upload-artifact: v2 -> v4
lukehesluke a695cb3
various improvements
lukehesluke 9bc8870
Merge branch 'feature/ci-test' into feature/ref-impl-db-2
lukehesluke eef2ba9
fix test
lukehesluke 91335ba
fix IdentityServer build error
lukehesluke dd048bc
Merge branch 'master' into feature/ref-impl-db-2
lukehesluke File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -336,3 +336,6 @@ ASALocalRun/ | |
|
||
# Fake database | ||
*fakedatabase.db | ||
|
||
# Output path for app publishing | ||
/web-app-package/ | ||
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
71 changes: 71 additions & 0 deletions
71
Examples/BookingSystem.AspNetCore/BackgroundServices/FakeDataRefresherService.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
using System; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This file is very similar to FakeDataRefresherService from #156, except with the following changes:
|
||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using Microsoft.Extensions.Hosting; | ||
using Microsoft.Extensions.Logging; | ||
using OpenActive.FakeDatabase.NET; | ||
using BookingSystem.AspNetCore.Services; | ||
|
||
|
||
namespace BookingSystem | ||
{ | ||
/// <summary> | ||
/// A background task which periodically refreshes the data in the | ||
/// FakeBookingSystem. This means that past data is deleted and new copies | ||
/// are created in the future. | ||
/// | ||
/// More information on background tasks here: | ||
/// https://docs.microsoft.com/en-us/dotnet/architecture/microservices/multi-container-microservice-net-applications/background-tasks-with-ihostedservice#implementing-ihostedservice-with-a-custom-hosted-service-class-deriving-from-the-backgroundservice-base-class | ||
/// </summary> | ||
public class FakeDataRefresherService : BackgroundService | ||
{ | ||
private readonly ILogger<FakeDataRefresherService> _logger; | ||
private readonly AppSettings _settings; | ||
private readonly FakeBookingSystem _bookingSystem; | ||
private readonly DataRefresherStatusService _statusService; | ||
|
||
public FakeDataRefresherService( | ||
AppSettings settings, | ||
ILogger<FakeDataRefresherService> logger, | ||
FakeBookingSystem bookingSystem, | ||
DataRefresherStatusService statusService) | ||
{ | ||
_settings = settings; | ||
_logger = logger; | ||
_bookingSystem = bookingSystem; | ||
_statusService = statusService; | ||
|
||
// Indicate that the refresher service is configured to run | ||
_statusService.SetRefresherConfigured(true); | ||
} | ||
|
||
protected override async Task ExecuteAsync(CancellationToken stoppingToken) | ||
{ | ||
var interval = TimeSpan.FromHours(_settings.DataRefresherIntervalHours); | ||
|
||
stoppingToken.Register(() => | ||
_logger.LogInformation($"FakeDataRefresherService background task is stopping.")); | ||
|
||
while (!stoppingToken.IsCancellationRequested) | ||
{ | ||
_logger.LogInformation($"FakeDataRefresherService is starting.."); | ||
var (numDeletedOccurrences, numDeletedSlots) = await _bookingSystem | ||
.Database | ||
.HardDeleteOldSoftDeletedOccurrencesAndSlots(); | ||
_logger.LogInformation($"FakeDataRefresherService hard deleted {numDeletedOccurrences} occurrences and {numDeletedSlots} slots that were previously old and soft-deleted."); | ||
|
||
var (numRefreshedOccurrences, numRefreshedSlots) = await _bookingSystem | ||
.Database | ||
.SoftDeletePastOpportunitiesAndInsertNewAtEdgeOfWindow(); | ||
_logger.LogInformation($"FakeDataRefresherService, for {numRefreshedOccurrences} old occurrences and {numRefreshedSlots} old slots, inserted new copies into the future and soft-deleted the old ones."); | ||
|
||
_logger.LogInformation($"FakeDataRefresherService is finished"); | ||
|
||
// Signal that a cycle has completed | ||
_statusService.SignalCycleCompletion(); | ||
|
||
await Task.Delay(interval, stoppingToken); | ||
} | ||
} | ||
} | ||
} |
76 changes: 76 additions & 0 deletions
76
Examples/BookingSystem.AspNetCore/Controllers/InitWaitController.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
using System; | ||
using System.Threading.Tasks; | ||
using BookingSystem.AspNetCore.Services; | ||
using Microsoft.AspNetCore.Mvc; | ||
using Microsoft.Extensions.Logging; | ||
|
||
/// <summary> | ||
/// Endpoints which are used to wait for components within | ||
/// BookingSystem.AspNetCore to be initialized. | ||
/// </summary> | ||
namespace BookingSystem.AspNetCore.Controllers | ||
{ | ||
[ApiController] | ||
[Route("init-wait")] | ||
public class InitWaitController : ControllerBase | ||
{ | ||
private readonly DataRefresherStatusService _statusService; | ||
private readonly ILogger<InitWaitController> _logger; | ||
private static readonly TimeSpan _defaultTimeout = TimeSpan.FromMinutes(5); | ||
|
||
public InitWaitController( | ||
DataRefresherStatusService statusService, | ||
ILogger<InitWaitController> logger) | ||
{ | ||
_statusService = statusService; | ||
_logger = logger; | ||
} | ||
|
||
/// <summary> | ||
/// Wait for the data refresher to complete its first cycle. | ||
/// | ||
/// This makes it possible to write scripts (for CI) which don't start | ||
/// until the data refresher has completed at least one cycle. | ||
/// | ||
/// - Returns 204 when the data refresher has completed its first cycle. | ||
/// - Returns 503 if the data refresher is not configured to run. | ||
/// - Returns 504 if the data refresher fails to complete a cycle within | ||
/// the default timeout. | ||
/// </summary> | ||
[HttpGet("data-refresher")] | ||
public async Task<IActionResult> WaitForDataRefresher() | ||
{ | ||
_logger.LogDebug("Received request to wait for data refresher completion"); | ||
|
||
// Check if the data refresher is configured to run | ||
if (!_statusService.IsRefresherConfigured()) | ||
{ | ||
_logger.LogWarning("Data refresher is not configured to run"); | ||
return StatusCode(503, "Data refresher service is not configured to run"); | ||
} | ||
|
||
// If it has already completed a cycle, return immediately | ||
if (_statusService.HasCompletedCycle()) | ||
{ | ||
_logger.LogDebug("Data refresher has already completed a cycle"); | ||
return NoContent(); | ||
} | ||
|
||
_logger.LogDebug("Waiting for data refresher to complete a cycle..."); | ||
|
||
// Wait for the cycle to complete, with a timeout | ||
await _statusService.WaitForCycleCompletion(_defaultTimeout); | ||
|
||
if (_statusService.HasCompletedCycle()) | ||
{ | ||
_logger.LogDebug("Data refresher completed a cycle, returning 204"); | ||
return NoContent(); | ||
} | ||
else | ||
{ | ||
_logger.LogWarning("Timed out waiting for data refresher to complete a cycle"); | ||
return StatusCode(504, "Timed out waiting for data refresher to complete a cycle"); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
59 changes: 59 additions & 0 deletions
59
Examples/BookingSystem.AspNetCore/Services/DataRefresherStatusService.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
using System; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
|
||
namespace BookingSystem.AspNetCore.Services | ||
{ | ||
/// <summary> | ||
/// A service which tracks the status of the data refresher, including | ||
/// whether it is configured to run and whether a cycle has completed. | ||
/// </summary> | ||
public class DataRefresherStatusService | ||
{ | ||
private readonly SemaphoreSlim _completionSemaphore = new SemaphoreSlim(0, 1); | ||
private bool _isRefresherConfigured = false; | ||
private bool _hasCompletedCycle = false; | ||
|
||
public void SetRefresherConfigured(bool isConfigured) | ||
{ | ||
_isRefresherConfigured = isConfigured; | ||
} | ||
|
||
public bool IsRefresherConfigured() | ||
{ | ||
return _isRefresherConfigured; | ||
} | ||
|
||
public void SignalCycleCompletion() | ||
{ | ||
_hasCompletedCycle = true; | ||
|
||
// Release the semaphore if someone is waiting on it | ||
if (_completionSemaphore.CurrentCount == 0) | ||
{ | ||
_completionSemaphore.Release(); | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Has the data refresher completed a cycle? | ||
/// | ||
/// This makes it possible to write scripts (for CI) which don't start | ||
/// until the data refresher has completed at least one cycle. | ||
/// </summary> | ||
public bool HasCompletedCycle() | ||
{ | ||
return _hasCompletedCycle; | ||
} | ||
|
||
public async Task WaitForCycleCompletion(TimeSpan timeout) | ||
{ | ||
if (_hasCompletedCycle) | ||
{ | ||
return; | ||
} | ||
|
||
await _completionSemaphore.WaitAsync(timeout); | ||
} | ||
} | ||
} |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this had accidentally entered the git repo at some point