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

Archiver Observability #3353

Merged
merged 20 commits into from
Nov 6, 2024
Merged

Archiver Observability #3353

merged 20 commits into from
Nov 6, 2024

Conversation

iancooper
Copy link
Member

We want to add observability to the archiver.

  • We call ExternalServiceBus.Archive from the OutboxArchiver. An interesting question here is: should we begin the trace in OutboxArchiver or within ExternalServiceBus. in this case we choose ExternalServiceBus, because it is the last possible moment that the span could begin, but is possible that we could argue to place in OutboxArchiver as the initiator.
  • There are no OTel semantic conventions for this, but we follow the same pattern as elsewhere: create span for the batch; archive batch for each item; use links when available to link the child spans back to the parent.

@iancooper iancooper added 2 - In Progress feature request v10 Allocal to a v10 release .NET Pull requests that update .net code labels Oct 15, 2024
@iancooper iancooper self-assigned this Oct 15, 2024
Copy link

@codescene-delta-analysis codescene-delta-analysis bot left a comment

Choose a reason for hiding this comment

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

Code Health Quality Gates: OK

Change in average Code Health of affected files: +0.10 (8.09 -> 8.19)

  • Declining Code Health: 1 findings(s) 🚩

View detailed results in CodeScene

@@ -41,6 +41,7 @@ public static class BrighterSpanExtensions
CommandProcessorSpanOperation.Publish => "publish",
CommandProcessorSpanOperation.Send => "send",
CommandProcessorSpanOperation.Clear => "clear",
CommandProcessorSpanOperation.Archive => "archive",

Choose a reason for hiding this comment

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

❌ New issue: Complex Method
ToSpanName has a cyclomatic complexity of 9, threshold = 9

Suppress

Copy link

@codescene-delta-analysis codescene-delta-analysis bot left a comment

Choose a reason for hiding this comment

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

Code Health Quality Gates: FAILED

Change in average Code Health of affected files: -0.04 (8.15 -> 8.12)

  • Declining Code Health: 4 findings(s) 🚩
  • Improving Code Health: 1 findings(s) ✅
  • Affected Hotspots: 1 files(s) 🔥

View detailed results in CodeScene

Comment on lines +287 to +292

var span = Tracer?.CreateDbSpan(
new OutboxSpanInfo(DbSystem.Brighter, InMemoryAttributes.DbName, OutboxDbOperation.DispatchedMessages, InMemoryAttributes.DbTable),
requestContext?.Span,
options: _instrumentationOptions
);

Choose a reason for hiding this comment

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

❌ New issue: Code Duplication
The module contains 4 functions with similar structure: DispatchedMessages,Get,MarkDispatched,OutstandingMessages

Suppress

@@ -214,6 +214,8 @@ public Task AddAsync(
IAmABoxTransactionProvider<CommittableTransaction>? transactionProvider = null,
CancellationToken cancellationToken = default)
{
//NOTE: As we call Add, don't create telemetry here

Choose a reason for hiding this comment

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

✅ Getting better: Missing Arguments Abstractions
The average number of function arguments decreases from 4.63 to 4.47, threshold = 4.00

@@ -214,6 +214,8 @@ public Task AddAsync(
IAmABoxTransactionProvider<CommittableTransaction>? transactionProvider = null,
CancellationToken cancellationToken = default)
{
//NOTE: As we call Add, don't create telemetry here

Choose a reason for hiding this comment

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

❌ New issue: Primitive Obsession
In this module, 30.3% of all function arguments are primitive types, threshold = 30.0%

Suppress

@@ -121,15 +121,14 @@ public void When_Clearing_A_Message_A_Span_Is_Exported()
_traceProvider.ForceFlush();

//assert
_exportedActivities.Count.Should().Be(7);
_exportedActivities.Count.Should().Be(8);

Choose a reason for hiding this comment

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

❌ Getting worse: Complex Method
When_Clearing_A_Message_A_Span_Is_Exported increases in cyclomatic complexity from 45 to 49, threshold = 9

Suppress

Copy link

@codescene-delta-analysis codescene-delta-analysis bot left a comment

Choose a reason for hiding this comment

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

Code Health Quality Gates: FAILED

Change in average Code Health of affected files: -0.05 (8.15 -> 8.11)

  • Declining Code Health: 5 findings(s) 🚩
  • Improving Code Health: 1 findings(s) ✅
  • Affected Hotspots: 1 files(s) 🔥

View detailed results in CodeScene

Copy link

@codescene-delta-analysis codescene-delta-analysis bot left a comment

Choose a reason for hiding this comment

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

Code Health Quality Gates: FAILED

Change in average Code Health of affected files: -0.07 (8.39 -> 8.32)

  • Declining Code Health: 6 findings(s) 🚩
  • Improving Code Health: 1 findings(s) ✅
  • Affected Hotspots: 1 files(s) 🔥

View detailed results in CodeScene

@@ -121,7 +121,7 @@ public async Task When_Clearing_A_Message_A_Span_Is_Exported()
_traceProvider.ForceFlush();

//assert
_exportedActivities.Count.Should().Be(7);
_exportedActivities.Count.Should().Be(8);

Choose a reason for hiding this comment

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

❌ Getting worse: Complex Method
When_Clearing_A_Message_A_Span_Is_Exported increases in cyclomatic complexity from 45 to 49, threshold = 9

Suppress

Copy link

@codescene-delta-analysis codescene-delta-analysis bot left a comment

Choose a reason for hiding this comment

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

Code Health Quality Gates: FAILED

Change in average Code Health of affected files: -0.05 (8.29 -> 8.24)

  • Declining Code Health: 8 findings(s) 🚩
  • Improving Code Health: 1 findings(s) ✅
  • Affected Hotspots: 2 files(s) 🔥

View detailed results in CodeScene

Copy link

@codescene-delta-analysis codescene-delta-analysis bot left a comment

Choose a reason for hiding this comment

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

Code Health Quality Gates: FAILED

Change in average Code Health of affected files: -0.05 (8.29 -> 8.24)

  • Declining Code Health: 8 findings(s) 🚩
  • Improving Code Health: 1 findings(s) ✅
  • Affected Hotspots: 2 files(s) 🔥

View detailed results in CodeScene

@iancooper iancooper marked this pull request as ready for review October 27, 2024 22:59
Copy link

@codescene-delta-analysis codescene-delta-analysis bot left a comment

Choose a reason for hiding this comment

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

Code Health Quality Gates: FAILED

Change in average Code Health of affected files: -0.03 (9.17 -> 9.13)

  • Declining Code Health: 9 findings(s) 🚩
  • Improving Code Health: 3 findings(s) ✅
  • Affected Hotspots: 2 files(s) 🔥

View detailed results in CodeScene

Comment on lines +90 to +158
[Fact]
public void When_archiving_from_the_outbox()
{
var parentActivity = new ActivitySource("Paramore.Brighter.Tests").StartActivity("BrighterTracerSpanTests");

var context = new RequestContext();
context.Span = parentActivity;

//add and clear message
var myEvent = new MyEvent();
var myMessage = new MyEventMessageMapper().MapToMessage(myEvent, _publication);
_bus.AddToOutbox(myMessage, context);
_bus.ClearOutbox([myMessage.Id], context);

//se should have an entry in the outbox
_outbox.EntryCount.Should().Be(1);

//allow time to pass
_timeProvider.Advance(TimeSpan.FromSeconds(300));

//archive
var dispatchedSince = TimeSpan.FromSeconds(100);
_archiver.Archive(dispatchedSince, context);

//should be no messages in the outbox
_outbox.EntryCount.Should().Be(0);

parentActivity?.Stop();

_traceProvider.ForceFlush();

//We should have exported matching activities
_exportedActivities.Count.Should().Be(9);

_exportedActivities.Any(a => a.Source.Name == "Paramore.Brighter").Should().BeTrue();

//there should be a n archive create span for the batch
var createActivity = _exportedActivities.Single(a => a.DisplayName == $"{BrighterSemanticConventions.ArchiveMessages} {CommandProcessorSpanOperation.Archive.ToSpanName()}");
createActivity.Should().NotBeNull();
createActivity.ParentId.Should().Be(parentActivity?.Id);

//check for outstanding messages span
var osCheckActivity = _exportedActivities.SingleOrDefault(a =>
a.DisplayName == $"{OutboxDbOperation.DispatchedMessages.ToSpanName()} {InMemoryAttributes.DbName} {InMemoryAttributes.DbTable}");
osCheckActivity.Should().NotBeNull();
osCheckActivity?.ParentId.Should().Be(createActivity.Id);

//check for delete messages span
var deleteActivity = _exportedActivities.SingleOrDefault(a =>
a.DisplayName == $"{OutboxDbOperation.Delete.ToSpanName()} {InMemoryAttributes.DbName} {InMemoryAttributes.DbTable}");
deleteActivity?.Should().NotBeNull();
deleteActivity?.ParentId.Should().Be(createActivity.Id);

//check the tags for the create span
createActivity.TagObjects.Should().Contain(t => t.Key == BrighterSemanticConventions.ArchiveAge && Math.Abs(Convert.ToDouble(t.Value) - dispatchedSince.TotalMilliseconds) < TOLERANCE);

//check the tags for the outstanding messages span
osCheckActivity?.Tags.Any(t => t.Key == BrighterSemanticConventions.DbOperation && t.Value == OutboxDbOperation.DispatchedMessages.ToSpanName()).Should().BeTrue();
osCheckActivity?.Tags.Any(t => t.Key == BrighterSemanticConventions.DbTable && t.Value == InMemoryAttributes.DbTable).Should().BeTrue();
osCheckActivity?.Tags.Any(t => t.Key == BrighterSemanticConventions.DbSystem && t.Value == DbSystem.Brighter.ToDbName()).Should().BeTrue();
osCheckActivity?.Tags.Any(t => t.Key == BrighterSemanticConventions.DbName && t.Value == InMemoryAttributes.DbName).Should().BeTrue();

//check the tages for the delete messages span
deleteActivity?.Tags.Any(t => t.Key == BrighterSemanticConventions.DbOperation && t.Value == OutboxDbOperation.Delete.ToSpanName()).Should().BeTrue();
deleteActivity?.Tags.Any(t => t.Key == BrighterSemanticConventions.DbTable && t.Value == InMemoryAttributes.DbTable).Should().BeTrue();
deleteActivity?.Tags.Any(t => t.Key == BrighterSemanticConventions.DbSystem && t.Value == DbSystem.Brighter.ToDbName()).Should().BeTrue();
deleteActivity?.Tags.Any(t => t.Key == BrighterSemanticConventions.DbName && t.Value == InMemoryAttributes.DbName).Should().BeTrue();

}

Choose a reason for hiding this comment

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

❌ New issue: Complex Method
When_archiving_from_the_outbox has a cyclomatic complexity of 10, threshold = 9

Suppress

Comment on lines +56 to +72
public OutboxArchiver(
IAmAnOutbox outbox,
IAmAnArchiveProvider archiveProvider,
IAmARequestContextFactory? requestContextFactory = null,
int archiveBatchSize = 100,
IAmABrighterTracer? tracer = null,
InstrumentationOptions instrumentationOptions = InstrumentationOptions.All)
{
_archiveProvider = archiveProvider;
_archiveBatchSize = archiveBatchSize;
_tracer = tracer;
_instrumentationOptions = instrumentationOptions;
_requestContextFactory = requestContextFactory ?? new InMemoryRequestContextFactory();

if (outbox is IAmAnOutboxSync<TMessage, TTransaction> syncOutbox) _outBox = syncOutbox;
if (outbox is IAmAnOutboxAsync<TMessage, TTransaction> asyncOutbox) _asyncOutbox = asyncOutbox;
}

Choose a reason for hiding this comment

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

❌ New issue: Constructor Over-Injection
OutboxArchiver has 6 arguments, threshold = 5

Suppress

@@ -993,10 +1028,10 @@ public void ClearOutstandingFromOutbox(
var subscription = _replySubscriptions?.FirstOrDefault(s => s.DataType == typeof(TResponse));

if (subscription is null)
throw new ArgumentOutOfRangeException($"No Subscription registered fpr replies of type {typeof(T)}");

Choose a reason for hiding this comment

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

❌ Getting worse: Code Duplication
introduced similar code in: ClearOutstandingFromOutbox

Suppress

Comment on lines +92 to +160
[Fact]
public async Task When_archiving_from_the_outbox()
{
var parentActivity = new ActivitySource("Paramore.Brighter.Tests").StartActivity("BrighterTracerSpanTests");

var context = new RequestContext();
context.Span = parentActivity;

//add and clear message
var myEvent = new MyEvent();
var myMessage = new MyEventMessageMapper().MapToMessage(myEvent, _publication);
await _bus.AddToOutboxAsync(myMessage, context);
await _bus.ClearOutboxAsync([myMessage.Id], context);

//se should have an entry in the outbox
_outbox.EntryCount.Should().Be(1);

//allow time to pass
_timeProvider.Advance(TimeSpan.FromSeconds(300));

//archive
var dispatchedSince = TimeSpan.FromSeconds(100);
await _archiver.ArchiveAsync(dispatchedSince, context);

//should be no messages in the outbox
_outbox.EntryCount.Should().Be(0);

parentActivity?.Stop();

_traceProvider.ForceFlush();

//We should have exported matching activities
_exportedActivities.Count.Should().Be(9);

_exportedActivities.Any(a => a.Source.Name == "Paramore.Brighter").Should().BeTrue();

//there should be a n archive create span for the batch
var createActivity = _exportedActivities.Single(a => a.DisplayName == $"{BrighterSemanticConventions.ArchiveMessages} {CommandProcessorSpanOperation.Archive.ToSpanName()}");
createActivity.Should().NotBeNull();
createActivity.ParentId.Should().Be(parentActivity?.Id);

//check for outstanding messages span
var osCheckActivity = _exportedActivities.SingleOrDefault(a =>
a.DisplayName == $"{OutboxDbOperation.DispatchedMessages.ToSpanName()} {InMemoryAttributes.DbName} {InMemoryAttributes.DbTable}");
osCheckActivity.Should().NotBeNull();
osCheckActivity?.ParentId.Should().Be(createActivity.Id);

//check for delete messages span
var deleteActivity = _exportedActivities.SingleOrDefault(a =>
a.DisplayName == $"{OutboxDbOperation.Delete.ToSpanName()} {InMemoryAttributes.DbName} {InMemoryAttributes.DbTable}");
deleteActivity.Should().NotBeNull();
deleteActivity?.ParentId.Should().Be(createActivity.Id);

//check the tags for the create span
createActivity.TagObjects.Should().Contain(t => t.Key == BrighterSemanticConventions.ArchiveAge && Math.Abs(Convert.ToDouble(t.Value) - dispatchedSince.TotalMilliseconds) < TOLERANCE);

//check the tags for the outstanding messages span
osCheckActivity?.Tags.Any(t => t.Key == BrighterSemanticConventions.DbOperation && t.Value == OutboxDbOperation.DispatchedMessages.ToSpanName()).Should().BeTrue();
osCheckActivity?.Tags.Any(t => t.Key == BrighterSemanticConventions.DbTable && t.Value == InMemoryAttributes.DbTable).Should().BeTrue();
osCheckActivity?.Tags.Any(t => t.Key == BrighterSemanticConventions.DbSystem && t.Value == DbSystem.Brighter.ToDbName()).Should().BeTrue();
osCheckActivity?.Tags.Any(t => t.Key == BrighterSemanticConventions.DbName && t.Value == InMemoryAttributes.DbName).Should().BeTrue();

//check the tages for the delete messages span
deleteActivity?.Tags.Any(t => t.Key == BrighterSemanticConventions.DbOperation && t.Value == OutboxDbOperation.Delete.ToSpanName()).Should().BeTrue();
deleteActivity?.Tags.Any(t => t.Key == BrighterSemanticConventions.DbTable && t.Value == InMemoryAttributes.DbTable).Should().BeTrue();
deleteActivity?.Tags.Any(t => t.Key == BrighterSemanticConventions.DbSystem && t.Value == DbSystem.Brighter.ToDbName()).Should().BeTrue();
deleteActivity?.Tags.Any(t => t.Key == BrighterSemanticConventions.DbName && t.Value == InMemoryAttributes.DbName).Should().BeTrue();

}

Choose a reason for hiding this comment

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

❌ New issue: Complex Method
When_archiving_from_the_outbox has a cyclomatic complexity of 10, threshold = 9

Suppress

Comment on lines +78 to +79
public OutboxProducerMediator(
IAmAProducerRegistry producerRegistry,

Choose a reason for hiding this comment

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

ℹ New issue: Complex Method
OutboxProducerMediator has a cyclomatic complexity of 14, threshold = 9

Comment on lines +78 to +79
public OutboxProducerMediator(
IAmAProducerRegistry producerRegistry,

Choose a reason for hiding this comment

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

ℹ New issue: Constructor Over-Injection
OutboxProducerMediator has 14 arguments, threshold = 5

Copy link

@codescene-delta-analysis codescene-delta-analysis bot left a comment

Choose a reason for hiding this comment

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

Code Health Quality Gates: FAILED

Change in average Code Health of affected files: -0.03 (9.17 -> 9.13)

  • Declining Code Health: 9 findings(s) 🚩
  • Improving Code Health: 3 findings(s) ✅
  • Affected Hotspots: 2 files(s) 🔥

View detailed results in CodeScene

Copy link

@codescene-delta-analysis codescene-delta-analysis bot left a comment

Choose a reason for hiding this comment

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

Code Health Quality Gates: FAILED

Change in average Code Health of affected files: -0.03 (9.17 -> 9.13)

  • Declining Code Health: 9 findings(s) 🚩
  • Improving Code Health: 3 findings(s) ✅
  • Affected Hotspots: 2 files(s) 🔥

View detailed results in CodeScene

Copy link

@codescene-delta-analysis codescene-delta-analysis bot left a comment

Choose a reason for hiding this comment

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

Code Health Quality Gates: FAILED

Change in average Code Health of affected files: -0.03 (9.17 -> 9.13)

  • Declining Code Health: 9 findings(s) 🚩
  • Improving Code Health: 4 findings(s) ✅
  • Affected Hotspots: 3 files(s) 🔥

View detailed results in CodeScene

@@ -325,17 +321,17 @@ private static object BuildCommandProcessor(IServiceProvider provider)
return commandProcessor;
}

private static IAmAnExternalBusService BuildExternalBus(IServiceProvider serviceProvider,

Choose a reason for hiding this comment

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

✅ No longer an issue: Excess Number of Function Arguments
BuildExternalBus is no longer above the threshold for number of arguments

@@ -325,17 +321,17 @@ private static object BuildCommandProcessor(IServiceProvider provider)
return commandProcessor;
}

private static IAmAnExternalBusService BuildExternalBus(IServiceProvider serviceProvider,
private static IAmAnOutboxProducerMediator BuildOutBoxProducerMediator(IServiceProvider serviceProvider,

Choose a reason for hiding this comment

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

ℹ New issue: Excess Number of Function Arguments
BuildOutBoxProducerMediator has 5 arguments, threshold = 4

Copy link

@codescene-delta-analysis codescene-delta-analysis bot left a comment

Choose a reason for hiding this comment

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

Code Health Quality Gates: FAILED

Change in average Code Health of affected files: -0.03 (9.17 -> 9.13)

  • Declining Code Health: 9 findings(s) 🚩
  • Improving Code Health: 4 findings(s) ✅
  • Affected Hotspots: 3 files(s) 🔥

View detailed results in CodeScene

@iancooper iancooper merged commit 262c7d7 into master Nov 6, 2024
16 of 20 checks passed
@iancooper iancooper deleted the arch_obser branch November 6, 2024 21:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
3 - Done feature request .NET Pull requests that update .net code v10 Allocal to a v10 release
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant