Skip to content
This repository has been archived by the owner on Aug 3, 2024. It is now read-only.
/ ServerCommon Public archive

[Package Signing] Added a Scoped Service Bus Message Handler #82

Merged
merged 4 commits into from
Nov 8, 2017

Conversation

loic-sharma
Copy link
Contributor

Often times, Service Bus message handlers will need to use some sort of Entity Framework context. This requires that the handlers' callbacks are executed within their own dependency injection scope. This change creates a new generic Service Bus message handler ScopedMessageHandler that, upon receiving a message, creates a new dependency injection scope.

This change also makes CachingSecretReader thread-safe, as otherwise, the ScopedMessageHandler would need a lock.

@loic-sharma loic-sharma self-assigned this Nov 8, 2017
@dnfclas
Copy link

dnfclas commented Nov 8, 2017

@loic-sharma,
Thanks for having already signed the Contribution License Agreement. Your agreement was validated by .NET Foundation. We will now review your pull request.
Thanks,
.NET Foundation Pull Request Bot

@loic-sharma loic-sharma changed the title [Package Signing] Added a Scoped MessageHandler [Package Signing] Added a Scoped Service Bus Message Handler Nov 8, 2017

public async Task<string> GetSecretAsync(string secretName)
{
// If the cache contains the secret and it is not expired, returned the cached value.
Copy link
Member

Choose a reason for hiding this comment

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

typo: returned > return

}

public async Task<string> GetSecretAsync(string secretName)
{
Copy link
Member

Choose a reason for hiding this comment

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

Should we have a null-check here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed

/// <summary>
/// A cached secret.
/// </summary>
private struct CachedSecret
Copy link
Member

Choose a reason for hiding this comment

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

Not sure whether struct is the right construct here, may be better off using class. This is not a short-lived object, is not immutable, and is likely larger than 16 bytes (DateTime is 8 bytes, string value most likely grows beyond 8 bytes - Unicode is 2 bytes per character).

https://docs.microsoft.com/en-us/dotnet/standard/design-guidelines/choosing-between-class-and-struct

Copy link
Contributor Author

@loic-sharma loic-sharma Nov 8, 2017

Choose a reason for hiding this comment

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

Hm. My gut feeling is that since we know the lifetime of the CachedSecret and the CachedSecret is immutable after creation, a struct is better here. I haven't measured performance though, so I'll play it safe and use a class instead.

P.S.: string is implemented as a DWORD and a WCHAR pointer, so a string is only 16 bytes on a 64-bit machine.

Copy link
Member

Choose a reason for hiding this comment

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

Additionally, it's an anti-pattern to make mutable structs. I agree, class is more appropriate here.


private readonly ISecretReader _internalReader;
private readonly Dictionary<string, Tuple<string, DateTime>> _cache;
private readonly ConcurrentDictionary<string, CachedSecret> _cache;
private readonly TimeSpan _refreshInterval;

public CachingSecretReader(ISecretReader secretReader, int refreshIntervalSec = DefaultRefreshIntervalSec)
Copy link
Member

Choose a reason for hiding this comment

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

Why doesn't this ctor just accept a TimeSpan?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, that'd be ideal. Unfortunately, that's a breaking change to a public API, and I don't know the implications of this. I'd like to do the minimal possible work possible to unblock package signing.

Copy link
Member

Choose a reason for hiding this comment

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

We are introducing a new type which must be adopted. Also, cleaning up our code will never be done unless make improvements along the way. Your call, tho.

{
throw new ArgumentNullException(nameof(secretReader));
if (!IsSecretOutdated(result))
Copy link
Member

Choose a reason for hiding this comment

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

nit nested if can be collapsed to a single if with an &&.

/// <summary>
/// The time at which the secret was cached.
/// </summary>
public DateTime CacheTimeUtc { get; set; }
Copy link
Member

Choose a reason for hiding this comment

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

Use DateTimeOffset so you don't need to specify UTC in the property name

public async Task<bool> HandleAsync(TMessage message)
{
// Create a new scope for this message.
using (var scope = _scopeFactory.CreateScope())
Copy link
Member

Choose a reason for hiding this comment

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

Looks great! 👍

Copy link
Member

@joelverhagen joelverhagen left a comment

Choose a reason for hiding this comment

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

Address comments then :shipit:

@loic-sharma loic-sharma merged commit b84a8d8 into dev Nov 8, 2017
@loic-sharma loic-sharma deleted the loshar-scoped-handler branch November 8, 2017 17:38
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants