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

updated service bus with new microsoft.azure.servicebus dotnet library #1

Merged
merged 21 commits into from
Nov 14, 2017

Conversation

shahbarkha
Copy link

@shahbarkha shahbarkha commented Aug 14, 2017

  • First cut that allows the foundatio.azureservicebus to use the latest azure service bus dotnet library ( 1.0.0)
  • Added 2 sample apps for topics and queues

@CLAassistant
Copy link

CLAassistant commented Aug 14, 2017

CLA assistant check
All committers have signed the CLA.

@niemyjski niemyjski requested a review from jamie94bc August 15, 2017 12:21
…exception. We instead call the overloaded method with no timeout in that case.
Copy link

@SeanFeldman SeanFeldman left a comment

Choose a reason for hiding this comment

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

@shahbarkha hope it helps



try {
var o = new Receiver();

Choose a reason for hiding this comment

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

"o"? 🙂

Copy link
Member

Choose a reason for hiding this comment

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

We can now do static async Task MainAsync and remove the Getawaiter().GetResult(). As this was added in the latest .net core..

Choose a reason for hiding this comment

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

You can also async Task Main() 🙂

Copy link
Author

Choose a reason for hiding this comment

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

Good tip Blake. I just realized i have removed the reference of sample project from the solution but didnt delete the sample files from the repo. Will be deleting all the samples. I think our unit test cases are great enough to test all the possible scenarios

using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

// General Information about an assembly is controlled through the following

Choose a reason for hiding this comment

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

remove commented out lines?

Copy link
Member

Choose a reason for hiding this comment

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

We should be able to drop all assembly info's as well or at least just provide a title and description as we do in the Foundatio Project (logging branch)

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>

Choose a reason for hiding this comment

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

You probably don't need this file.

Copy link
Member

Choose a reason for hiding this comment

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

We should be able to remove the app.config completely in all projects

Copy link
Author

Choose a reason for hiding this comment

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

I have actually deleted the sample project itself. I didn't find any value to that when we have good amount of unit test cases covering all the test cases.

<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6" />
</startup>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">

Choose a reason for hiding this comment

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

Can be replaced entirely with

    <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
    <GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType>

@@ -0,0 +1,83 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>

Choose a reason for hiding this comment

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

Can be entirely replaced by PackageReference in csproj.
The benefit is you can reduce number of transitive packages listed as dependencies.
E.g.: xunit has a dependency on xunit.assert and xunit.core which do not need to be referenced if using PackageReference

Copy link
Member

Choose a reason for hiding this comment

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

If we switch to the new project system I don't think we can remove most of these references. We can do this in a second pr as well.

private async Task OnMessageAsync(BrokeredMessage brokeredMessage) {
// Use this Handler to look at the exceptions received on the MessagePump
private Task OnExceptionAsync(ExceptionReceivedEventArgs args) {
_logger.Warn(args.Exception, "Message handler encountered an exception.");

Choose a reason for hiding this comment

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

Do not swallow args. It contains valuable information valuable information such as

  • action that failed
  • entity path
  • namespace
    It all should be logged imo

Copy link
Member

Choose a reason for hiding this comment

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

+1, We upgraded to the new Microsoft logging in foundatio 6. I can pair on this if you'd like

if (_subscribers.IsEmpty)
return;

_logger.Trace("OnMessageAsync({messageId})", brokeredMessage.MessageId);
_logger.Trace($"Received message: messageId:{ brokeredMessage.MessageId} SequenceNumber:{brokeredMessage.SystemProperties.SequenceNumber}");

Choose a reason for hiding this comment

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

MessageId can be null. You want to wrap it with some sort of delimiters to avoid confusion in the output.
Eg: $"Received message: messageId:'{brokeredMessage.MessageId}' SequenceNumber....

if (!String.IsNullOrEmpty(_options.TopicUserMetadata))
td.UserMetadata = _options.TopicUserMetadata;
//if (!String.IsNullOrEmpty(_options.TopicUserMetadata))
// td.UserMetadata = _options.TopicUserMetadata;

Choose a reason for hiding this comment

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

If disabled because of issues in GH (like this one), ideally would be good to see the issue URL in a comment.

Copy link
Member

Choose a reason for hiding this comment

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

@shahbarkha Azure/azure-sdk-for-net#3635 Any idea if this is now supported?

Copy link
Author

Choose a reason for hiding this comment

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

@niemyjski Its still not supported!!

);

// If the token isn't a valid string, throw an error.
if (String.IsNullOrEmpty(result.AccessToken)) {

Choose a reason for hiding this comment

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

String.IsNullOrEmpty

should be

string.IsNullOrEmpty

Copy link
Author

Choose a reason for hiding this comment

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

@SeanFeldman Thanks Sean for reviewing this. I know I am asking for too much but can you please also take a glance at the functionality on how I am calling AbandonAsync, CompleteAsync using 2 different objects ( IQueueclient and IMessageReceiver )

await _queueClient.CompleteAsync(entry.Id).AnyContext();
await _messageReceiver.CompleteAsync(entry.Id).AnyContext();

As you noticed this library has 2 ways of pulling the items from the queue:

  1. Push Strategy - Which IQueueClient provides by registering RegisterMessageHandler.
  2. Pull Strategy - Which IMessageReceive provides using ReceiveAsync.

For some reason, in the new azure service bus dot net core library ReceiveAsync method has been removed from the IQueueClient and that leaves me to use the IMessageReceiver for fetching the item from the queue for the 2nd strategy.

Even though the endpoint for the queue is same if I try to call AbandonAsyc using the IMessageReceiver object for the queue item that has been dequeued using IQueueClient, I get "MessageLockLostException". Similary, If i try to call AbandonAsync using the IQueueClient object for the queue item that has been dequeued using IMessageReceiver, then also i get the same exception.
Because of this reason, I have to maintain some metadata in the QueueEntry to know if the queue item was dequeued using push or pull strategy so that I can call the AbandonAsync or CompleteAsync and even RenewLockAsync using appropriate objects ( either IQueueClient or IMessageReceiver ).

Do you know by any chance why there is a difference in behavior ? Shouldnt there be no exception if i try to call any of the above methods with any object.

Choose a reason for hiding this comment

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

The older version of the library used SBMP by default. The new one is using AMQP. With SBMP you could receive a message with one receiver and complete with another where with AMQP there's a receiver affinity. Meaning you have to complete/abandon/renew your message using the same receiver used to get the message to begin with.

As for the MessageAPI (registering message handler) and ReceiveAsync, it's all there, just slightly reshuffled. ReceiveAsync is now on MessageReceiver as it's considered a more "advanced" option (you control number of messages, write your own message pump, etc) and QueueClient is more of a "simple" scenario, intended to be used with the OOTB message pump registered with RegisterSessionHandler.

Copy link
Member

Choose a reason for hiding this comment

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

AMQP there's a receiver affinity. Is that for AMQP in general?

Choose a reason for hiding this comment

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

For the ASB .NET Standard client. Don't know if that's a general AMQP requirement or not.

@shahbarkha
Copy link
Author

shahbarkha commented Oct 30, 2017 via email

Copy link
Member

@niemyjski niemyjski left a comment

Choose a reason for hiding this comment

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

Looking good, There are some small things that need to be updated like the tests calling the wrong methods and I'm not sure if a queue instance is being returned. I know some of that is timeout related and we probably need to address that in the base test harness (making a timeout passed in, or we correct the min timespan passed in to us to be the min azure allowed timespan if 0 is passed). It looks like the foundatio lib needs to be updated to the latest version (6.0). I can help with that if you think it's good to be merged in. We could merge it to a branch and then rework logging and the configuration system.

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
Copy link
Member

Choose a reason for hiding this comment

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

We should be able to remove the app.config completely in all projects

<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Foundatio.TestHarness" Version="5.1.1474" />
Copy link
Member

Choose a reason for hiding this comment

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

We should pull in Foundatio 6.0. We just need to add the following nuget config (https://github.com/FoundatioFx/Foundatio.Redis/blob/feature/logging/NuGet.config minus the loresoft on line 5)

Copy link
Author

Choose a reason for hiding this comment

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

I have removed the sample projects. Now I am referencing Foundatio 5.1.1562 which is the latest and the greatest. I dont see 6.0 in the Updates at all. Am I missing something



try {
var o = new Receiver();
Copy link
Member

Choose a reason for hiding this comment

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

We can now do static async Task MainAsync and remove the Getawaiter().GetResult(). As this was added in the latest .net core..

using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

// General Information about an assembly is controlled through the following
Copy link
Member

Choose a reason for hiding this comment

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

We should be able to drop all assembly info's as well or at least just provide a title and description as we do in the Foundatio Project (logging branch)

class Receiver {
private async Task TestTopic() {
IMessageBus messageBus =
new AzureServiceBusMessageBus(new AzureServiceBusMessageBusOptions() {
Copy link
Member

Choose a reason for hiding this comment

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

If we update to the latest foundatio 6, it brings in the logging changes and we've migrated everything to the new cross plat configuration system). We can accept the pr as is but if you wanted to tackle that it's pretty fun to play with and learn :)

if (_topicClient == null)
return;

_topicClient?.Close();
await _topicClient?.CloseAsync();
Copy link
Member

Choose a reason for hiding this comment

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

Need to ensure we have AnyContext() on all awaited async methods.

LoggerFactory = Log
});
}
//protected override IQueue<SimpleWorkItem> GetQueue(int retries = 0, TimeSpan? workItemTimeout = null, TimeSpan? retryDelay = null, int deadLetterMaxItems = 100, bool runQueueMaintenance = true) {
Copy link
Member

Choose a reason for hiding this comment

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

Are these tests even running since there is no Queue implementation being returned?

Copy link
Author

Choose a reason for hiding this comment

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

Yes they are all running with actual Queue parameters. They are commented out because they need to be supplied with the actual values mentioned in the configuration file. Once they are supplied all tests pass. If they are commented then the base queue implementation is returned. One needs to uncomment them for all the ASB specific tests to run.

[Fact]
public override Task WillWaitForItemAsync() {
return base.WillWaitForItemAsync();
return WillWaitItemAsync();
Copy link
Member

Choose a reason for hiding this comment

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

This is calling the wrong test method

Copy link
Author

Choose a reason for hiding this comment

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

I had to override the method because base class has some timeout values which does not work for ASB. Like you mentioned we need to supply min and max timeout value.

public override Task WillNotWaitForItemAsync() {
return base.WillNotWaitForItemAsync();
return DontWaitForItemAsync();
Copy link
Member

Choose a reason for hiding this comment

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

This is calling the wrong test method

public override Task CanRenewLockAsync() {
return base.CanRenewLockAsync();
return RenewLockAsync();
Copy link
Member

Choose a reason for hiding this comment

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

This is calling the wrong test method

-Added base project for benchmark. Still need to write code.
-Removed the files from the Sample project.
- Using Microsoft.Extensions.Logging for logging
- Fixed unit test for service message bus
@@ -73,44 +71,34 @@ public class AzureServiceBusMessageBus : MessageBusBase<AzureServiceBusMessageBu

_subscriptionClient.RegisterMessageHandler(OnMessageAsync, new MessageHandlerOptions(OnExceptionAsync) { MaxConcurrentCalls = maxConcurrentCalls, AutoComplete = autoComplete });
sw.Stop();
_logger.Trace("Ensure topic subscription exists took {0}ms.", sw.ElapsedMilliseconds);
if (_logger.IsEnabled(LogLevel.Trace)) _logger.LogTrace("Ensure topic subscription exists took {ElapsedMilliseconds}", sw.ElapsedMilliseconds);
Copy link
Member

Choose a reason for hiding this comment

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

Looks good, I learned yesterday that if you do "{Duration:g}", sw.Elapsed you get really nice formatting for timespans :)

Copy link
Author

Choose a reason for hiding this comment

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

will make these changes. Thank you

_queueClient.RegisterMessageHandler(async (msg, token) => {
_logger.Trace("WorkerLoop Signaled {_options.Name}", _options.Name);
if (_logger.IsEnabled(LogLevel.Trace)) _logger.LogTrace("WorkerLoop Signaled {.Name}", _options.Name);
Copy link
Member

Choose a reason for hiding this comment

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

We got an extra . in here by accident

Copy link
Author

Choose a reason for hiding this comment

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

fixed

@@ -165,7 +170,7 @@ public class AzureServiceBusQueueTests : QueueTestBase {
var sw = Stopwatch.StartNew();
var workItem = await queue.DequeueAsync(TimeSpan.FromMilliseconds(100));
sw.Stop();
_logger.Trace("Time {0}", sw.Elapsed);
if (_logger.IsEnabled(LogLevel.Trace)) _logger.LogTrace("Time {Elapsed}", sw.Elapsed);
Copy link
Member

Choose a reason for hiding this comment

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

Should add the formatter for Elapsed to {Duration:g}

Copy link
Member

@niemyjski niemyjski left a comment

Choose a reason for hiding this comment

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

LGTM!!!!

@niemyjski
Copy link
Member

Looks great!! Awesome job. Do you feel like we should merge it in or merge it into a branch? Guess we could merge it in and just bump the major again or put it in preview until the management stuff gets done

@niemyjski
Copy link
Member

@SeanFeldman would you mind reviewing this and leaving your comments to my last question :) Thanks in advance

@niemyjski
Copy link
Member

Let me know when you want me to merge this in :)

@shahbarkha
Copy link
Author

@niemyjski I liked your idea of putting this stuff on the preview untill the management stuff is released. Also I want to do the benchmark thing going on before we merge it in the master.

@niemyjski niemyjski changed the base branch from master to feature/netstandard November 14, 2017 11:26
@niemyjski niemyjski merged commit fa28eab into FoundatioFx:feature/netstandard Nov 14, 2017
@niemyjski
Copy link
Member

niemyjski commented Nov 14, 2017

Thanks for the PR! I've merged this into the .net standard branch. We'll push a preview build out shortly. We might have to wait until the logging release goes out as that's going to be a stable build and then this one so it's a higher nightly preview build or bump the major on this

@SeanFeldman
Copy link

@SeanFeldman would you mind reviewing this and leaving your comments to my last question :) Thanks in advance

Sorry @niemyjski, was away on vacation. Looks like a bit late to review 😃

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.

4 participants