Skip to content
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

EES-5826 - use channels to separate Public API query response from the writing of queries for analytics #5678

Open
wants to merge 1 commit into
base: dev
Choose a base branch
from

Conversation

duncan-at-hiveit
Copy link
Collaborator

This PR:

  • replaces the fire-and-forget implementation of the writing out of Public API queries for analytics with a channel implementation.

Rather than spawning off a separate Task from the main request-processing Thread and simply not awaiting it, instead we provide a Channel whereby the DataSetQueryService can write the desired query to be captured to the channel.

A BackgroundService continually monitors the Channel, and reads from the Channel when new messages are available. It then forwards the message to the QueryAnalyticsWriter service, which then performs the actual work of writing out the query for analytics.

@duncan-at-hiveit duncan-at-hiveit force-pushed the EES-5826-use-channels-for-fire-and-forget branch from 81d9c6e to 9307ef6 Compare March 11, 2025 09:38
…sts for writing queries for analytics, rather than not awaiting the writing process.
@duncan-at-hiveit duncan-at-hiveit force-pushed the EES-5826-use-channels-for-fire-and-forget branch from 9307ef6 to f2a8898 Compare March 11, 2025 09:40
Copy link
Collaborator

@leeoades leeoades left a comment

Choose a reason for hiding this comment

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

This is very nice!
A few comments but will leave to your discretion. Let me know if you produce anything else for me to review.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Unless you think it provides added clarity, I wouldn't put Channel in the name since that is an implementation detail. You can think if this more as the QueryAnalyticsReporter or Manager or Handler? ie it's just the place where all other components can go to send/report their analytics.

while (!stoppingToken.IsCancellationRequested)
{
var message = await channel.ReadQuery(stoppingToken);
await queryAnalyticsWriter.ReportDataSetVersionQuery(message);
Copy link
Collaborator

Choose a reason for hiding this comment

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

If this throws, I'm guessing this background service will stop writing the files? Probably want to guard against that - and log errors?

@@ -47,15 +30,15 @@ public async Task ReportDataSetVersionQuery(

Directory.CreateDirectory(directory);
Copy link
Collaborator

Choose a reason for hiding this comment

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

I would catch and log errors here - permissions, disk full, all sorts of things that could go wrong.


public ValueTask<CaptureDataSetVersionQueryRequest> ReadQuery(CancellationToken cancellationToken)
{
return default;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Could this potentially lead to a spinning process? If something was consuming it, it would be pouring out a stream of values? Perhaps add :
await Task.Delay(Timeout.Infinite, cancellationToken);

Copy link
Collaborator

Choose a reason for hiding this comment

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

Not something for you to change - but just whilst I have your attention :-)
It would be usual for a there to be two interfaces, one as the "Reporter" where components can send their data, and another as a "Consumer" that would take the data out and process it. This cleans up the intentions of components and prevents any possibility of anything else reading from the channel when they should only ever be writing. The container of course resolves to the same instance.

IQueryAnalyticsWriter queryAnalyticsWriter
) : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can this be unit tested?


namespace GovUk.Education.ExploreEducationStatistics.Public.Data.Api.Services;

public class QueryAnalyticsChannel : IQueryAnalyticsChannel
Copy link
Collaborator

Choose a reason for hiding this comment

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

Unit tests for this class?

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.

2 participants