Skip to content

Appt xxx/add point read slide cache site#1633

Draft
pata9 wants to merge 23 commits intomainfrom
APPT-XXX/add_point_read_slide_cache_site
Draft

Appt xxx/add point read slide cache site#1633
pata9 wants to merge 23 commits intomainfrom
APPT-XXX/add_point_read_slide_cache_site

Conversation

@pata9
Copy link
Copy Markdown
Contributor

@pata9 pata9 commented Apr 21, 2026

Description

Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context. List any dependencies that are required for this change.

Fixes # (issue)

Checklist:

  • My work is behind a feature toggle (if appropriate)
  • If my work is behind a feature toggle, I've added a full suite of tests for both the ON and OFF state
  • The ticket number is in the Pull Request title, with format "APPT-XXX: My Title Here"
  • I have ran npm tsc / lint (in the future these will be ran automatically)
  • My code generates no new .NET warnings (in the future these will be treated as errors)
  • If I've added a new Function, it is disabled in all but one of the terraform groups (e.g. http_functions)
  • If I've added a new Function, it has both unit and integration tests. Any request body validators have unit tests also
  • If I've made UI changes, I've added appropriate Playwright and Jest tests
  • If I've added/updated an end-point, I've added the appropriate annotations and tested the Swagger documentation reflects the change

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

{
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?

{
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

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?

$"site:referenceNumberGroup:{siteId}",
new CacheOptions<int>(
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 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

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)

var referenceNumberGroup =
await _cacheService.GetCacheValue(
$"site:referenceNumberGroup:{siteId}",
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


public interface ISiteService
{
Task<Site> GetSiteByIdAsync(string siteId, 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.

Ignore cache should be the last param and it should be a enum.

enum should be called something like - DataSource
enum values should follow something like - "UseDatabase", "UseCache"

}
else
{
site = await cacheService.GetLazySlidingCacheValue($"site:{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.

cache key factory here

{
var site = await siteStore.GetSiteById(siteId);
Site site;
if (options.Value.DisableSiteCache || ignoreCache)
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.

Invert if and move and introduce a private method for use cache

/// <summary>
/// The duration, in minutes, to cache site for.
/// </summary>
public int SiteCacheDurationMinutes { get; set; }
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.

Add getter to expose these int's as timespan that can then be unit tested

[CosmosDocumentType("reference_group")]
public class BookingReferenceGroupDocument : BookingReferenceDataCosmosDocument
{
public int SiteCount { get; set; }
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.

Debate should prefix be explicit or use Id as this implementation does

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.

Maybe just expose it here?


referenceGroupDocuments = (await _cosmosStore.RunQueryAsync<BookingReferenceGroupDocument>(x => x.DocumentType == docType)).ToArray();

if (!referenceGroupDocuments.Any())
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.

Count vs any

}

var target = referenceGroupDocuments.Where(g => g.Id != 0.ToString()).OrderBy(g => g.SiteCount).ThenBy(g => g.Id).First();
var siteCountIncrement = PatchOperation.Increment($"/SiteCount", 1);
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.

Magic strings to static class

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.

Object orientated? ReferenceGroupOperation.SiteIncrement() OR ReferenceGroupOperation.UpdateSiteCount()

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.

Maybe should live on the BookingReferenceGroupDocument


public async Task<int> GetNextSequenceNumber(int prefix)
{
var incrementSequencePatch = PatchOperation.Increment($"/Sequence", 1);
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.

Magic string!


export const fetchSite = async (
siteId: string,
ignoreCache: boolean,
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.

Use the enum like in the API layor

isDeleted: dataTable.GetBoolRowValueOrDefault(row, "IsDeleted"),
Type: dataTable.GetRowValueOrDefault(row, "Type")
Type: dataTable.GetRowValueOrDefault(row, "Type"),
ReferenceNumberGroup: 0
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.

Can we do this once somewhere please?

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.

Maybe use -1 by default

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant