Skip to content
Draft
Show file tree
Hide file tree
Changes from 17 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions infrastructure/environments/perf-ukw/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,9 @@ module "mya_application_perf" {
cosmos_aggregation_autoscale_settings = [{
max_throughput = 10000
}]
cosmos_booking_reference_data_autoscale_settings = [{
max_throughput = 25000
}]
cosmos_core_autoscale_settings = [{
max_throughput = 25000
}]
Expand Down
3 changes: 3 additions & 0 deletions infrastructure/environments/perf/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,9 @@ module "mya_application_perf" {
cosmos_aggregation_autoscale_settings = [{
max_throughput = 10000
}]
cosmos_booking_reference_data_autoscale_settings = [{
max_throughput = 25000
}]
cosmos_core_autoscale_settings = [{
max_throughput = 25000
}]
Expand Down
3 changes: 3 additions & 0 deletions infrastructure/environments/prod-ukw/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,9 @@ module "mya_application_prod_ukw" {
cosmos_aggregation_autoscale_settings = [{
max_throughput = 10000
}]
cosmos_booking_reference_data_autoscale_settings = [{
max_throughput = 25000
}]
cosmos_core_autoscale_settings = [{
max_throughput = 25000
}]
Expand Down
3 changes: 3 additions & 0 deletions infrastructure/environments/prod/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,9 @@ module "mya_application_prod" {
cosmos_aggregation_autoscale_settings = [{
max_throughput = 10000
}]
cosmos_booking_reference_data_autoscale_settings = [{
max_throughput = 25000
}]
cosmos_core_autoscale_settings = [{
max_throughput = 25000
}]
Expand Down
3 changes: 3 additions & 0 deletions infrastructure/environments/stag-ukw/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,9 @@ module "mya_application_stag_ukw" {
cosmos_aggregation_autoscale_settings = [{
max_throughput = 10000
}]
cosmos_booking_reference_data_autoscale_settings = [{
max_throughput = 25000
}]
cosmos_core_autoscale_settings = [{
max_throughput = 25000
}]
Expand Down
3 changes: 3 additions & 0 deletions infrastructure/environments/stag/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,9 @@ module "mya_application_stag" {
cosmos_aggregation_autoscale_settings = [{
max_throughput = 10000
}]
cosmos_booking_reference_data_autoscale_settings = [{
max_throughput = 25000
}]
cosmos_core_autoscale_settings = [{
max_throughput = 25000
}]
Expand Down
16 changes: 16 additions & 0 deletions infrastructure/resources/cosmosdb.tf
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,22 @@ resource "azurerm_cosmosdb_sql_container" "nbs_mya_core_container" {
}
}

resource "azurerm_cosmosdb_sql_container" "nbs_mya_booking_reference_container" {
count = var.create_cosmos_db ? 1 : 0
name = "booking_reference_data"
resource_group_name = local.resource_group_name
account_name = azurerm_cosmosdb_account.nbs_mya_cosmos_db[0].name
database_name = azurerm_cosmosdb_sql_database.nbs_appts_database[0].name
partition_key_paths = ["/docType"]

dynamic "autoscale_settings" {
for_each = var.cosmos_booking_reference_data_autoscale_settings
content {
max_throughput = autoscale_settings.value["max_throughput"]
}
}
}

resource "azurerm_cosmosdb_sql_container" "nbs_mya_aggregation_container" {
count = var.create_cosmos_db ? 1 : 0
name = "aggregated_data"
Expand Down
4 changes: 4 additions & 0 deletions infrastructure/resources/high_load_functions.tf
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ resource "azurerm_windows_function_app" "nbs_mya_high_load_func_app" {
ALLSITES_SLIDING_CACHE_ENABLED = var.allsites_sliding_cache_enabled
ALLSITES_CACHE_DURATION_MINUTES = var.allsites_cache_duration_minutes
ALLSITES_SLIDING_CACHE_DURATION_MINUTES = var.allsites_sliding_cache_duration_minutes
SITE_CACHE_DURATION_MINUTES = var.site_cache_duration_minutes
SITE_SLIDING_CACHE_DURATION_MINUTES = var.site_sliding_cache_duration_minutes
DISABLE_SITE_CACHE = var.disable_site_cache
SITE_SUPPORTS_SERVICE_SLIDING_CACHE_ABSOLUTE_EXPIRATION_SECONDS = var.site_supports_service_sliding_cache_absolute_expiration_seconds
SITE_SUPPORTS_SERVICE_SLIDING_CACHE_SLIDE_THRESHOLD_SECONDS = var.site_supports_service_sliding_cache_slide_threshold_seconds
Expand Down Expand Up @@ -205,6 +207,8 @@ resource "azurerm_windows_function_app_slot" "nbs_mya_high_load_func_app_preview
ALLSITES_SLIDING_CACHE_ENABLED = var.allsites_sliding_cache_enabled
ALLSITES_CACHE_DURATION_MINUTES = var.allsites_cache_duration_minutes
ALLSITES_SLIDING_CACHE_DURATION_MINUTES = var.allsites_sliding_cache_duration_minutes
SITE_CACHE_DURATION_MINUTES = var.site_cache_duration_minutes
SITE_SLIDING_CACHE_DURATION_MINUTES = var.site_sliding_cache_duration_minutes
DISABLE_SITE_CACHE = var.disable_site_cache
SITE_SUPPORTS_SERVICE_SLIDING_CACHE_ABSOLUTE_EXPIRATION_SECONDS = var.site_supports_service_sliding_cache_absolute_expiration_seconds
SITE_SUPPORTS_SERVICE_SLIDING_CACHE_SLIDE_THRESHOLD_SECONDS = var.site_supports_service_sliding_cache_slide_threshold_seconds
Expand Down
4 changes: 4 additions & 0 deletions infrastructure/resources/http_functions.tf
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ resource "azurerm_windows_function_app" "nbs_mya_http_func_app" {
ALLSITES_SLIDING_CACHE_ENABLED = var.allsites_sliding_cache_enabled
ALLSITES_CACHE_DURATION_MINUTES = var.allsites_cache_duration_minutes
ALLSITES_SLIDING_CACHE_DURATION_MINUTES = var.allsites_sliding_cache_duration_minutes
SITE_CACHE_DURATION_MINUTES = var.site_cache_duration_minutes
SITE_SLIDING_CACHE_DURATION_MINUTES = var.site_sliding_cache_duration_minutes
DISABLE_SITE_CACHE = var.disable_site_cache
SITE_SUPPORTS_SERVICE_SLIDING_CACHE_ABSOLUTE_EXPIRATION_SECONDS = var.site_supports_service_sliding_cache_absolute_expiration_seconds
SITE_SUPPORTS_SERVICE_SLIDING_CACHE_SLIDE_THRESHOLD_SECONDS = var.site_supports_service_sliding_cache_slide_threshold_seconds
Expand Down Expand Up @@ -183,6 +185,8 @@ resource "azurerm_windows_function_app_slot" "nbs_mya_http_func_app_preview" {
ALLSITES_SLIDING_CACHE_ENABLED = var.allsites_sliding_cache_enabled
ALLSITES_CACHE_DURATION_MINUTES = var.allsites_cache_duration_minutes
ALLSITES_SLIDING_CACHE_DURATION_MINUTES = var.allsites_sliding_cache_duration_minutes
SITE_CACHE_DURATION_MINUTES = var.site_cache_duration_minutes
SITE_SLIDING_CACHE_DURATION_MINUTES = var.site_sliding_cache_duration_minutes
DISABLE_SITE_CACHE = var.disable_site_cache
SITE_SUPPORTS_SERVICE_SLIDING_CACHE_ABSOLUTE_EXPIRATION_SECONDS = var.site_supports_service_sliding_cache_absolute_expiration_seconds
SITE_SUPPORTS_SERVICE_SLIDING_CACHE_SLIDE_THRESHOLD_SECONDS = var.site_supports_service_sliding_cache_slide_threshold_seconds
Expand Down
4 changes: 4 additions & 0 deletions infrastructure/resources/servicebus_functions.tf
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ resource "azurerm_windows_function_app" "nbs_mya_service_bus_func_app" {
ALLSITES_SLIDING_CACHE_ENABLED = var.allsites_sliding_cache_enabled
ALLSITES_CACHE_DURATION_MINUTES = var.allsites_cache_duration_minutes
ALLSITES_SLIDING_CACHE_DURATION_MINUTES = var.allsites_sliding_cache_duration_minutes
SITE_CACHE_DURATION_MINUTES = var.site_cache_duration_minutes
SITE_SLIDING_CACHE_DURATION_MINUTES = var.site_sliding_cache_duration_minutes
DISABLE_SITE_CACHE = var.disable_site_cache
CANCEL_A_DATE_RANGE_MAXIMUM_DAYS = var.cancel_a_date_range_maximum_days
APPLICATION_NAME = "Service Bus Function App"
Expand Down Expand Up @@ -185,6 +187,8 @@ resource "azurerm_windows_function_app_slot" "nbs_mya_service_bus_func_app_previ
ALLSITES_SLIDING_CACHE_ENABLED = var.allsites_sliding_cache_enabled
ALLSITES_CACHE_DURATION_MINUTES = var.allsites_sliding_cache_duration_minutes
ALLSITES_SLIDING_CACHE_DURATION_MINUTES = var.allsites_cache_duration_minutes
SITE_CACHE_DURATION_MINUTES = var.site_cache_duration_minutes
SITE_SLIDING_CACHE_DURATION_MINUTES = var.site_sliding_cache_duration_minutes
DISABLE_SITE_CACHE = var.disable_site_cache
SITE_SUMMARY_DAYS_FORWARD = var.site_summary_days_forward
SITE_SUMMARY_DAYS_CHUNK_SIZE = var.site_summary_days_chunk_size
Expand Down
4 changes: 4 additions & 0 deletions infrastructure/resources/timer_functions.tf
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ resource "azurerm_windows_function_app" "nbs_mya_timer_func_app" {
ALLSITES_SLIDING_CACHE_ENABLED = var.allsites_sliding_cache_enabled
ALLSITES_CACHE_DURATION_MINUTES = var.allsites_cache_duration_minutes
ALLSITES_SLIDING_CACHE_DURATION_MINUTES = var.allsites_sliding_cache_duration_minutes
SITE_CACHE_DURATION_MINUTES = var.site_cache_duration_minutes
SITE_SLIDING_CACHE_DURATION_MINUTES = var.site_sliding_cache_duration_minutes
DISABLE_SITE_CACHE = var.disable_site_cache
SPLUNK_HOST_URL = var.splunk_host_url
SPLUNK_HEC_TOKEN = var.splunk_hec_token
Expand Down Expand Up @@ -195,6 +197,8 @@ resource "azurerm_windows_function_app_slot" "nbs_mya_timer_func_app_preview" {
ALLSITES_SLIDING_CACHE_ENABLED = var.allsites_sliding_cache_enabled
ALLSITES_CACHE_DURATION_MINUTES = var.allsites_sliding_cache_duration_minutes
ALLSITES_SLIDING_CACHE_DURATION_MINUTES = var.allsites_cache_duration_minutes
SITE_CACHE_DURATION_MINUTES = var.site_cache_duration_minutes
SITE_SLIDING_CACHE_DURATION_MINUTES = var.site_sliding_cache_duration_minutes
DISABLE_SITE_CACHE = var.disable_site_cache
SPLUNK_HOST_URL = var.splunk_host_url
SPLUNK_HEC_TOKEN = var.splunk_hec_token
Expand Down
15 changes: 15 additions & 0 deletions infrastructure/resources/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,11 @@ variable "cosmos_core_autoscale_settings" {
default = []
}

variable "cosmos_booking_reference_data_autoscale_settings" {
type = list(any)
default = []
}

variable "cosmos_index_autoscale_settings" {
type = list(any)
default = []
Expand Down Expand Up @@ -476,6 +481,16 @@ variable "allsites_sliding_cache_duration_minutes" {
default = 20
}

variable "site_cache_duration_minutes" {
type = number
default = 20
}

variable "site_sliding_cache_duration_minutes" {
type = number
default = 10
}

variable "auditor_enable" {
type = bool
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ private static async Task SetupCosmosDatabase(CosmosClient cosmosClient)
var database = await cosmosClient.CreateDatabaseIfNotExistsAsync(id: "appts");
await database.Database.CreateContainerIfNotExistsAsync(id: "booking_data", partitionKeyPath: "/site");
await database.Database.CreateContainerIfNotExistsAsync(id: "core_data", partitionKeyPath: "/docType");
await database.Database.CreateContainerIfNotExistsAsync(id: "booking_reference_data", partitionKeyPath: "/docType");
await database.Database.CreateContainerIfNotExistsAsync(id: "index_data", partitionKeyPath: "/docType");
await database.Database.CreateContainerIfNotExistsAsync(id: "audit_data", partitionKeyPath: "/user");
await database.Database.CreateContainerIfNotExistsAsync(id: "aggregated_data", partitionKeyPath: "/date");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public override Task<IActionResult> RunAsync(

protected override async Task<ApiResult<Site>> HandleRequest(SiteBasedResourceRequest request, ILogger logger)
{
var site = await siteService.GetSiteByIdAsync(request.Site, request.Scope);
var site = await siteService.GetSiteByIdAsync(request.Site, request.IgnoreCache, request.Scope);
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IgnoreCache should be last param for this method

if (site != null)
{
return ApiResult<Site>.Success(site);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ protected override async Task<ApiResult<GetSiteMetaDataResponse>> HandleRequest(
ILogger logger)
{
const string scope = "site_details";
var site = await siteService.GetSiteByIdAsync(request.Site, scope);
var site = await siteService.GetSiteByIdAsync(request.Site, false, scope);
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this function is not used? Can we just delete this?

if (site != null)
{
var patientInformation = site.Accessibilities.Any()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,15 @@ public abstract class SiteBasedResourceFunction<TResponse>(IValidator<SiteBasedR
protected override Task<(IReadOnlyCollection<ErrorMessageResponseItem> errors, SiteBasedResourceRequest request)> ReadRequestAsync(HttpRequest req)
{
var requestedScope = req.Query.Keys.Contains("scope") ? req.Query["scope"].ToString() : "*";
var requestedIgnoreCache = req.Query.Keys.Contains("ignoreCache") && bool.Parse(req.Query["ignoreCache"]);

if (req.Query.Keys.Contains("site"))
{
var site = req.Query["site"];
return Task.FromResult((ErrorMessageResponseItem.None, new SiteBasedResourceRequest(site, requestedScope)));
return Task.FromResult((ErrorMessageResponseItem.None, new SiteBasedResourceRequest(site, requestedIgnoreCache, requestedScope)));
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IgnoreCache should be the last param here

}

var siteId = RestUriHelper.GetResourceIdFromPath(req.Path.ToUriComponent(), "sites");
return Task.FromResult((ErrorMessageResponseItem.None, new SiteBasedResourceRequest(siteId, requestedScope)));
return Task.FromResult((ErrorMessageResponseItem.None, new SiteBasedResourceRequest(siteId, requestedIgnoreCache, requestedScope)));
}
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
namespace Nhs.Appointments.Api.Models;

public record SiteBasedResourceRequest(string Site, string Scope);
public record SiteBasedResourceRequest(string Site, bool IgnoreCache, string Scope);
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move to last param and maybe default to false?

Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public async Task Notify(string eventType, string user, string siteId, string[]
var rolesAddedNames = GetRoleNames(roles, rolesAdded);
var rolesRemovedNames = GetRoleNames(roles, rolesRemoved);

var site = await siteService.GetSiteByIdAsync(siteId, "*");
var site = await siteService.GetSiteByIdAsync(siteId);
var siteName = site == null ? $"Unknown site ({siteId})" : site.Name;

var templateValues = new Dictionary<string, dynamic>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace Nhs.Appointments.Core.Bookings;

public interface IReferenceNumberDocumentStore
{
Task<int> AssignReferenceGroup();
Task<int> GetNextSequenceNumber(int prefix);
}
49 changes: 32 additions & 17 deletions src/api/Nhs.Appointments.Core/Bookings/ReferenceNumberProvider.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using Nhs.Appointments.Core.Caching;
using Nhs.Appointments.Core.Sites;

namespace Nhs.Appointments.Core.Bookings;
Expand All @@ -9,37 +10,51 @@ public interface IReferenceNumberProvider

public class ReferenceNumberProvider : IReferenceNumberProvider
{
private readonly ISiteStore _siteStore;
private readonly ISiteService _siteService;
private readonly ICacheService _cacheService;
private readonly IReferenceNumberDocumentStore _referenceNumberDocumentStore;
private readonly TimeProvider _timeProvider;
public ReferenceNumberProvider(
ISiteStore siteStore,
ISiteService siteService,
ICacheService cacheService,
IReferenceNumberDocumentStore referenceNumberDocumentStore,
TimeProvider timeProvider)
{
_siteStore = siteStore;
_siteService = siteService;
_cacheService = cacheService;
_referenceNumberDocumentStore = referenceNumberDocumentStore;
_timeProvider = timeProvider;
}
public async Task<string> GetReferenceNumber(string siteId)
{
var referenceGroup = await _siteStore.GetReferenceNumberGroup(siteId);
if (referenceGroup == 0)
{
referenceGroup = await _referenceNumberDocumentStore.AssignReferenceGroup();
await _siteStore.AssignPrefix(siteId, referenceGroup);
}
{
var referenceNumberGroup =
await _cacheService.GetCacheValue(
$"site:referenceNumberGroup:{siteId}",
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resolve cache key from a factory

new CacheOptions<int>(
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We thought about this and decided there's no real benefit to sliding cache here. use the default long lived cache for less complexity

async () => await GetSitesAssignedReferenceGroup(siteId),
TimeSpan.FromHours(24)));
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Configure expiration in app settings


var sequence = await _referenceNumberDocumentStore.GetNextSequenceNumber(referenceGroup);
var sequence = await _referenceNumberDocumentStore.GetNextSequenceNumber(referenceNumberGroup);
var now = _timeProvider.GetUtcNow();
var rng = now.Day + now.Second;
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another level of randomness here (90-99 are not being used currently)


return $"{referenceGroup:00}-{rng:00}-{sequence:000000}";
return $"{referenceNumberGroup:00}-{rng:00}-{sequence:000000}";
}
}

private async Task<int> GetSitesAssignedReferenceGroup(string siteId)
{
var site = await _siteService.GetSiteByIdAsync(siteId, true);
if (!SiteHasNotBeenAssignedReferenceGroup(site))
{
return site.ReferenceNumberGroup;
}

public interface IReferenceNumberDocumentStore
{
Task<int> AssignReferenceGroup();
Task<int> GetNextSequenceNumber(int prefix);
var referenceGroup = await _referenceNumberDocumentStore.AssignReferenceGroup();
await _siteService.AssignPrefix(siteId, referenceGroup);

return referenceGroup;

}

private static bool SiteHasNotBeenAssignedReferenceGroup(Site site) => site.ReferenceNumberGroup == 0;
}
2 changes: 2 additions & 0 deletions src/api/Nhs.Appointments.Core/ServiceRegistration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ public static IServiceCollection ConfigureSiteService(this IServiceCollection se
opts.AllSitesSlidingCacheEnabled = configuration.GetValue("ALLSITES_SLIDING_CACHE_ENABLED", true);
opts.AllSitesCacheDurationMinutes = configuration.GetValue("ALLSITES_CACHE_DURATION_MINUTES", 60);
opts.AllSitesSlideCacheDurationMinutes = configuration.GetValue("ALLSITES_SLIDING_CACHE_DURATION_MINUTES", 20);
opts.SiteCacheDurationMinutes = configuration.GetValue("SITE_CACHE_DURATION_MINUTES", 5);
opts.SiteSlideCacheDurationMinutes = configuration.GetValue("SITE_SLIDING_CACHE_DURATION_MINUTES", 1);
opts.DisableSiteCache = configuration.GetValue("DISABLE_SITE_CACHE", false);
//default 4 hours
opts.SiteSupportsServiceSlidingCacheAbsoluteExpirationSeconds = configuration.GetValue("SITE_SUPPORTS_SERVICE_SLIDING_CACHE_ABSOLUTE_EXPIRATION_SECONDS", 14400);
Expand Down
1 change: 0 additions & 1 deletion src/api/Nhs.Appointments.Core/Sites/ISiteStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ Task<OperationResult> UpdateSiteDetails(string siteId, string name, string addre
Task<OperationResult> UpdateSiteReferenceDetails(string siteId, string odsCode, string icb, string region);

Task AssignPrefix(string site, int prefix);
Task<int> GetReferenceNumberGroup(string site);
Task<IEnumerable<Site>> GetAllSites();

Task<OperationResult> SaveSiteAsync(string siteId, string odsCode, string name, string address, string phoneNumber,
Expand Down
6 changes: 5 additions & 1 deletion src/api/Nhs.Appointments.Core/Sites/Site.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,17 @@ public record Site(
[JsonProperty("location")] Location location,
[JsonProperty("status")] SiteStatus? status,
[JsonProperty("isDeleted")] bool? isDeleted,
[JsonProperty("type")] string Type
[JsonProperty("type")] string Type,
int ReferenceNumberGroup
)
{
public IEnumerable<Accessibility> Accessibilities { get; set; } = Accessibilities?.Select(a => new Accessibility(a.Id, a.Value.ToLower()));

private Location Location { get; } = location;

[JsonIgnore]
public int ReferenceNumberGroup { get; } = ReferenceNumberGroup;

public Coordinates Coordinates
{
get
Expand Down
Loading