Azure Service Bus fails with NOTFOUND exception - node.js

I'm iterating over 400,000 json messages that need to get sent from my NodeJS Azure Function, into Azure Service Bus. The Function is able to create the topic, and start publishing messages.
It starts to go through the loop and publish messages. I see a couple thousand land in the queue before the publish fails with the following error:
{
Error: getaddrinfo ENOTFOUND ABC.servicebus.windows.net ABC.servicebus.windows.net:443 at errnoException (dns.js:53:10) at GetAddrInfoReqWrap.onlookup [as oncomplete] (dns.js:95:26)
code: 'ENOTFOUND',
errno: 'ENOTFOUND',
syscall: 'getaddrinfo',
hostname: 'ABC.servicebus.windows.net',
host: 'ABC.servicebus.windows.net',
port: '443'
}
My code to publish the messages composes a message, and pushes it through the JS API. The body of the message is a small JSON object:
{
"adult":false,
"id":511351,
"original_title":"Nossa Carne de Carnaval",
"popularity":0,
"video":false
}
The method in my Azure Function that is pushing this message into the Service Bus is as follows:
function publishNewMovies(context, movies) {
var azure = require('azure');
var moment = require('moment');
var topic = process.env["NewMovieTopic"];
var connectionString = process.env["AZURE_SERVICEBUS_CONNECTION_STRING"];
context.log("Configuring Service Bus.");
return new Promise((resolve, reject) => {
var serviceBusService = azure.createServiceBusService(connectionString);
serviceBusService.createTopicIfNotExists(topic, function(error) {
if (error) {
context.log(`Failed to get the Service Bus topic`);
reject(error);
}
context.log("Service Bus setup.");
// Delay the receiving of these messages by 5 minutes on any subscriber.
var scheduledDate = moment.utc();
scheduledDate.add('5', 'minutes');
context.log("Sending new movie messages.");
var message = {
body: '',
customProperties: {
messageNumber: 0
},
brokerProperties: {
ScheduledEnqueueTimeUtc: scheduledDate.toString()
}
}
for(index = 0; index < movies.length; index += 40) {
message.brokerProperties.ScheduledEnqueueTimeUtc = scheduledDate.add('11', 'seconds').toString();
for(batchIndex = 0; batchIndex < 40; batchIndex++) {
var currentIndex = index + batchIndex;
if (currentIndex >= movies.length) {
break;
}
message.customProperties.messageNumber = currentIndex;
message.body = JSON.stringify(movies[currentIndex]);
serviceBusService.sendTopicMessage(topic, message, function(error) {
if (error) {
context.log(`Failed to send topic message for ${message.body}: ${error}`);
reject(error);
}
})
}
}
});
});
}
This creates a message that is visible starting 5 minutes from the first Service Bus push. Then I batch send 40 messages for that scheduled time. Once the first batch is done, I schedule another 40 messages 11 seconds into the future. This is because there is another Azure Function that will be written that is going to listen to this Service Bus topic and make 3rd party API requests. I'm rate limited at 40 messages every 10 seconds.
I've read through the Azure Functions documentation on quotas and it looks like I might be hitting the 10,000 topic/queue limit. With this limit, how is someone supposed to push out large quantities of messages into the bus? Setting up multiple namespaces to get around this seems backwards when I'm sending the same message, just with different content - it belongs in the same namespace. The error that I'm receiving doesn't indicate I'm hitting a limit or anything. It sounds like it's failing to find the actual service-bus end-point for some reason.
Is the answer to this to handle partitioning? I can't find documentation on how to handle partitioning with NodeJs. Most of their API documentation is in C# which doesn't hasn't translate to NodeJs well for me.
Edit to show Bus metrics
Bus Metrics
Topic Metrics

Can you elaborate why you are actually creating so many topics?
This:
var topic = process.env["NewMovieTopic"];
you can have 1 topic which can get millions of messages which then would be transferred to the individual subscriptions which you would add filter criteria too. So there should be no need for so many topics.
Usually topics, subscriptions and queues would be created in the management plane (portal, arm, PS or cli) runtime or data operations would be functions, cloud apps, VMs, so Service bus likely can easily handle your volume unless you have a very specific reason for creating these many topics?

Related

Azure Functions + EventHub: why batch latency grows up constantly?

I have next chart:
As you can see my batch latency grows up and count of outgoing messages grows down.
Inside of the function I do append to a blob storage. But blob metrics says everything is ok.
What could be causing the ever-increasing latency?
Function implementation:
const parsedEvents = eventHubMessages.map((event) => {
try {
return JSON.parse(event);
} catch (error) {
context.log(`Error: cannot parse next event: ${event}`);
return {};
}
});
for (const event of parsedEvents) {
const { id } = event;
const data = {
data: 'data',
};
const filename = `${id}.log`;
await blob.append(filename, JSON.stringify(data));
}
Blob append is a instance of a class and looks like:
class AzureStorage {
constructor(config) {
this.config = config;
this.blobServiceClient = BlobServiceClient.fromConnectionString(this.config.storageConnectionString);
this.containerClient = this.blobServiceClient.getContainerClient(this.config.containerName);
}
async append(filename, data) {
const client = this.containerClient.getAppendBlobClient(filename);
await client.createIfNotExists();
await client.appendBlock(data, data.length);
}
}
Another one chart:
Update:
So, my problem was in the blob storage. I did client.createIfNotExists(); and this is the root of the problem. I rewrite my code next way:
I call client.appendBlock
I catch it and if there is an error, then I do client.create(); and then client.appendBlock one more time.
Thanks #JesseSquire for your helpful suggestion. Adding few more troubleshooting points that helps to find the root cause of latency issues in Azure Functions integrated with Event Hubs.
Also check if versioning is enabled on the Storage account which may slows down considerably.
Make sure you can scale out to at least the number of partitions your Event Hub has by checking your Function scaling setup.
Use Logging/Application Insights feature to measure the execution time for blob append in order to check bottlenecks in your code.
Telemetry Logs helps you to find your function performance data & metrics for avoiding the integrated services like Event Hub batch events latency issues, runtime exceptions, etc.
Dedicated storage account is better because of checkpointing, event hub-triggered functions could experience a large volume of storage transactions.
Refer to the MS Doc of Azure Functions Performance for Event Hubs.

Unable to configure Azure Event Hub Producer

I am trying a sample code of Azure Event Hub Producer and trying to send some message to Azure Event Hub.
The eventhub and its policy is correctly configured for sending and listening messages. I am using Dotnet core 3.1 console application. However, the code doesn't move beyond CreateBatchAsync() call. I tried debugging and the breakpoint doesn't go to next line. Tried Try-catch-finally and still no progress. Please guide what I am doing wrong here. The Event hub on Azure is shows some number of successful incoming requests.
class Program
{
private const string connectionString = "<event_hub_connection_string>";
private const string eventHubName = "<event_hub_name>";
static async Task Main()
{
// Create a producer client that you can use to send events to an event hub
await using (var producerClient = new EventHubProducerClient(connectionString, eventHubName))
{
// Create a batch of events
using EventDataBatch eventBatch = await producerClient.CreateBatchAsync();
// Add events to the batch. An event is a represented by a collection of bytes and metadata.
eventBatch.TryAdd(new EventData(Encoding.UTF8.GetBytes("First event")));
eventBatch.TryAdd(new EventData(Encoding.UTF8.GetBytes("Second event")));
eventBatch.TryAdd(new EventData(Encoding.UTF8.GetBytes("Third event")));
// Use the producer client to send the batch of events to the event hub
await producerClient.SendAsync(eventBatch);
Console.WriteLine("A batch of 3 events has been published.");
}
}
}
The call to CreateBatchAsync would be the first need to create a connection to Event Hubs. This indicates that you're likely experiencing a connectivity or authorization issue.
In the default configuration you're using, the default network timeout is 60 seconds and up to 3 retries are possible, with some back-off between them.
Because of this, a failure to connect or authorize may take up to roughly 5 minutes before it manifests. That said, the majority of connection errors are not eligible for retries, so the failure would normally surface after roughly 1 minute.
To aid in your debugging, I'd suggest tweaking the default retry policy to speed things up and surface an exception more quickly so that you have the information needed to troubleshoot and make adjustments. The options to do so are discussed in this sample and would look something like:
var connectionString = "<< CONNECTION STRING FOR THE EVENT HUBS NAMESPACE >>";
var eventHubName = "<< NAME OF THE EVENT HUB >>";
var options = new EventHubProducerClientOptions
{
RetryOptions = new EventHubsRetryOptions
{
// Allow the network operation only 15 seconds to complete.
TryTimeout = TimeSpan.FromSeconds(15),
// Turn off retries
MaximumRetries = 0,
Mode = EventHubsRetryMode.Fixed,
Delay = TimeSpan.FromMilliseconds(10),
MaximumDelay = TimeSpan.FromSeconds(1)
}
};
await using var producer = new EventHubProducerClient(
connectionString,
eventHubName,
options);

How do I make sure to receive all of my messages with Azure Service Bus Queue?

I created a Service Bus Queue following the tutorial in Microsoft Documentation. I can send and receive messages, however, only half of my messages make it through. Literally half, only the even ones.
I tried changing the message frequency but it doesn't change anything. It doesn't matter if I send a message every 3 seconds or 3 messages per second, I only get half of them on the other end.
I have run the example code in all the possible languages and I have tried using the REST API and batch messaging but no dice.
I also tried using Azure Functions with the specific trigger for Service Bus Queues.
This is the receiving function code:
module.exports = async function(context, mySbMsg) {
context.log('JavaScript ServiceBus queue trigger function processed message', mySbMsg);
context.done();
};
And this is the send function code:
module.exports = async function (context, req) {
context.log('JavaScript HTTP trigger function processed a request.');
var azure = require('azure-sb');
var idx = 0;
function sendMessages(sbService, queueName) {
var msg = 'Message # ' + (++idx);
sbService.sendQueueMessage(queueName, msg, function (err) {
if (err) {
console.log('Failed Tx: ', err);
} else {
console.log('Sent ' + msg);
}
});
}
var connStr = 'Endpoint=sb://<sbnamespace>.servicebus.windows.net/;SharedAccessKeyName=<keyname>;SharedAccessKey=<key>';
var queueName = 'MessageQueue';
context.log('Connecting to ' + connStr + ' queue ' + queueName);
var sbService = azure.createServiceBusService(connStr);
sbService.createQueueIfNotExists(queueName, function (err) {
if (err) {
console.log('Failed to create queue: ', err);
} else {
setInterval(sendMessages.bind(null, sbService, queueName), 2000);
}
});
};
I expect to receive most of the sent messages (specially in this conditions of no load at all) but instead I only receive 50%.
My guess is that the reason is that you are only listening to one of 2 subscriptions on the topic and it is set up to split the messages between subscriptions. This functionality is used to split workload to multiple services. You can read about topics here: https://learn.microsoft.com/en-us/azure/service-bus-messaging/service-bus-messaging-overview
and
https://learn.microsoft.com/en-us/azure/service-bus-messaging/topic-filters
Here sort description from above links:
"Partitioning uses filters to distribute messages across several existing topic subscriptions in a predictable and mutually exclusive manner. The partitioning pattern is used when a system is scaled out to handle many different contexts in functionally identical compartments that each hold a subset of the overall data; for example, customer profile information. With partitioning, a publisher submits the message into a topic without requiring any knowledge of the partitioning model. The message then is moved to the correct subscription from which it can then be retrieved by the partition's message handler."
To check this you can see if your service bus have partitioning turned on or any other filters.Turning partitioning off should do the trick in your case I think.

Event emitter loop not working in node-red function

I am attempting to use a node-red function to read Azure IoT Hub messages using AMQP. I have imported the azure-iothub module.
The code below connects ok and getFeedbackReceiver returns an AmqpReceiver object (I can see the object output to debug tab) which acts as an event emitter. However, the emitter loop (msgReceiver.on) doesn't seem to run. I don't even get a null output to debug tab.
Any help appreciated.
var azureamqp = global.get('azureamqp');
var iothub = azureamqp.Client.fromConnectionString(cnct);
try
{
iothub.open(function()
{
node.send({type: "connection", payload: util.inspect(iothub)});
node.status({ fill: "green", shape: "ring", text: "listening" });
iothub.getFeedbackReceiver(function(err,msgReceiver)
{
if (!err)
{
node.send({type: "receiver", payload: util.inspect(msgReceiver)});
msgReceiver.on('message',function(message)
{
node.send({payload:message});
});
}
else
{
node.send({payload: err});
}
});
node.send({type: "streamend"});
node.status({ fill: "red", shape: "ring", text: "disconnected" });
});
}
catch (err)
{}
Ok I think I see what's happening thanks to the additional comments: First of, some reference docs about messaging with IoT Hub:
https://learn.microsoft.com/en-us/azure/iot-hub/iot-hub-devguide-messaging
There are 2 types of messages that are sent through IoT Hub, depending on the direction in which they go:
Cloud to Device (C2D, or commands): these messages are sent by a cloud app to one or more devices. They are stored in a device-specific queue, and delivered to the device as soon as the device connects and starts listening for messages. Once received by the device, the device can choose to send feedback about these to IoT Hub. This feedback is received using the azure-iothub.Client.getFeedbackReceiver() API. I've added more about this at the end of the answer.
Device-to-Cloud (D2C, or telemetry): these messages are sent by the devices to the cloud application. These messages are stored in Event Hubs partitions and can be read from these partitions. This happens with the Event Hubs SDK
From the question and the comments it looks like you're trying to receive D2C messages (telemetry) using the feedback API - and it's not working, because it's not meant to work that way. It's good feedback for (bad?) API design and lack of docs though.
How to receive messages sent by devices:
This sample on github shows how to set up an Event Hubs client and can be used to listen to messages sent by devices to IoT Hub.This command in iothub-explorer is also an easy reference.
Please find below a simple example:
'use strict';
var EventHubClient = require('azure-event-hubs').Client;
var Promise = require('bluebird');
var connectionString = '[IoT Hub Connection String]';
var client = EventHubClient.fromConnectionString(connectionString);
var receiveAfterTime = Date.now() - 5000;
var printError = function (err) {
console.error(err.message);
};
var printEvent = function (ehEvent) {
console.log('Event Received: ');
console.log(JSON.stringify(ehEvent.body));
console.log('');
};
client.open()
.then(client.getPartitionIds.bind(client))
.then(function (partitionIds) {
return Promise.map(partitionIds, function (partitionId) {
return client.createReceiver('$Default', partitionId, { 'startAfterTime' : receiveAfterTime}).then(function(receiver) {
receiver.on('errorReceived', printError);
receiver.on('message', printEvent);
});
});
}).catch(printError);
A little more about that code sample:
Basically, the Event Hubs client provide an AMQP connection can be used to open receivers on each partition of the Event Hub. Partitions are used to store messages sent by devices. Each partition gets its own receiver, and each receiver has a message event. Hence the need to open one receiver per partition to never miss any message from any devices. Here's a little bit more about Event Hubs and the nature of partitions: https://learn.microsoft.com/en-us/azure/event-hubs/event-hubs-what-is-event-hubs
More about C2D Feedback:
There are 3 types of feedback that a device can send about a C2D message:
accept (or complete) means the message is taken care of by the device and the IoT Hub will remove this message from the device queue
reject means that the device doesn't want this message (maybe because it's malformed, or irrelevant, that's up to you to device) and the IoT Hub will remove the message from the queue.
abandon means that the device cannot "take care" of this message right now and wants IoT Hub to resend it later. The message remains in the queue.
accept and reject are both going to be observed using the getFeedbackReceiver API, as well as timeouts if the message is never received or is abandonned too many times.

Receive only the last message on topics

I have an application subscribed on Azure Servicebus Topic who is constantly receiving messages from Stream Analytics. But this application isn't every time subscribed on this Topic. How do I receive only the last message from the topic when the application do the subscription?
Based on your question and your comments, this is what I can advice you:
When your application starts, connect to the Azure ServiceBus Subscription and get all messages in the queue.
Remove all the previous messages (just complete it) and process the last message.
Then you can start listening to new incoming messages.
Based on this answer (Clearing azure service bus queue in one go) :
// Get the message receiver
var messagingFactory = MessagingFactory.CreateFromConnectionString("ServiceBusConnectionString");
var messageReceiver = messagingFactory.CreateMessageReceiver(SubscriptionClient.FormatSubscriptionPath("TopicName", "SubscriptionName"));
BrokeredMessage lastMessage = null;
while (messageReceiver.Peek() != null)
{
if(lastMessage != null)
{
// This was not the last message so complete it.
lastMessage.Complete();
}
// Batch the receive operation
var brokeredMessages = messageReceiver.ReceiveBatch(300).ToList();
//Get the last message and remove it from the list
lastMessage = brokeredMessages.Last();
brokeredMessages.RemoveAt(brokeredMessages.Count -1);
// Complete all the other messages
var completeTasks = brokeredMessages.Select(m => Task.Run(() => m.Complete())).ToArray();
// Wait for the tasks to complete.
Task.WaitAll(completeTasks);
}
if (lastMessage != null)
{
// Process your message
}
// Start listening to new incoming message
messageReceiver.OnMessage(message =>
{
// Process new messages
}, new OnMessageOptions());

Resources