Persist queue: serialize/deserialize queue object in node-amqp - node.js

I'm using the node-amqp module to manage rabbitmq subscriptions. Specifically, I'm assigning an exclusive/private queue to each user/session, and providing binding methods through the REST interface. I.e. "bind my queue to this exchange/routing_key pair", and "unbind my queue to this exchange/routing_key pair".
The challenge here is to avoid keeping a reference to the queue object in memory (say, in an object with module-wide scope).
Simply retrieving the queue itself from the connection each time I need it, proved difficult, since the queue object keeps tabs on bindings internally, probably to avoid violating the following from the amqp 0.9.1 reference:
The client MUST NOT attempt to unbind a queue that does not exist. Error code: not-found
I tried to simply set the queue object as a property on a session object using connect-mongo, since it uses JSON.stringify/JSON.parse on its properties. Unfortunately, the queue object fails to "stringify" due to a circular structure.
What is the best practice for persisting a queue object from the node-amqp module? Is it possible to serialize/deserialize?

I would not try to store the queue object, instead of that use an unique name for the queue that you can store. After that whenever you want to make operations over the queue you have two options:
In the case you have a previously opened "channel" to the queue, you should be able to do:
queue = connection.queues[name].
I mean connection as a node-amqp connection against rabbitMQ.
In the case you dont have a channel opened in your connection with rabbitmq, just open the channel again:
connection.queue(name = queueName, options, function(queue) {
// for example do unbind
})
I am also using REST interface to manage rabbitMQ. My connection object maintains all the queues, channels, etc... So, only the first time I try to use a queue I call to connection.queue, and the following request just retrieve the queue through connection.queues.

Related

Get the data from the thread

Let me give you a bigger picture of the problem... I am designing a ROS2-based system with multiple ROS2 nodes each containing a wrapper part (ROS2 layer) and driver/module part where my low-level logic is implemented. The wrapper part is using some ROS2-specific communication mechanisms (topics, services, actions...) to exchange the data/commands between the nodes.
Now, one of the nodes in the system should establish an MQTT connection with the Google Cloud Platform, keep the connectivity alive and allow data exchange between the Cloud and ROS2 system. For that purpose, I am using iot-device-sdk-embedded-c SDK from Google.
It has iotc_connect() blocking function for establishing and keeping connection with the Cloud so the challenge I am facing with is to simultaneously keep the ROS2 node spinning while keeping MQTT connectivity alive.
My idea was to launch a thread from ROS2 wrapper that will be used for establishing/keeping MQTT connectivity and use a callback function as an argument for the thread function that will enable me to forward the data received from the Cloud ithin the thread directly to ROS2 layer. Launching a separate thread for handling connectivity and data exchange would enable my ROS2 node to properly spin and rest synchronized with the rest of the ROS2 system.
ROS2_Wrapper.cpp
thread mqtt_thread(MqttConnHandler::ConnectToMqttServer, &MqttThreadCallback);
mqtt_thread.detach();
...
void MqttThreadCallback(void* data, size_t size){
}
MqttThreadCallback() should be called every time I receive the command/config data from the Cloud.
However, I am not sure how can I fire the callback function within the thread because I have two layers of nested callbacks within the thread:
my_thread.cpp
ConnectToMqttServer(void (*MqttThreadCallback)(void*, size_t)){
...
iotc_connect(...,&OnConnectionStateChanged);
...
}
OnConnectionStateChanged(...){
...
case IOTC_CONNECTION_STATE_OPENED:
iotc_subscribe(...,&iotc_mqttlogic_subscribe_callback,...);
...
}
iotc_mqttlogic_subscribe_callback(...){
//The place where data from the Cloud are received
}
iotc_connect() contains OnConnectionStateChanged() callback from where iotc_subscribe() function is called at the moment connection is established. iotc_subscribe() contains iotc_mqttlogic_subscribe_callback() where data from the Cloud are received.
I am not sure how can I mount the data from iotc_mqttlogic_subscribe_callback() up to the thread caller. Do you have any suggestions? Perhaps using the threads is not the best approach?
Usually C libraries provide an optional additional argument called user_data for this purpose:
extern iotc_state_t iotc_subscribe(iotc_context_handle_t iotc_h,
const char* topic, const iotc_mqtt_qos_t qos,
iotc_user_subscription_callback_t* callback,
void* user_data);
That way you can cast your callback function pointer to void when calling subscribe and catch it as argument in the iotc_mqttlogic_subscribe_callback function call. Where you should recast the data back to the function pointer type and use it.
In addition, you may find yourself in need to pass more data to the callback (mutex to protect the data, loggers from higher level code...). In that case, the best practice is to wrap all this info in a new class of your choice and pass a pointer to the instance in the callback.

ServiceStack SSE OnJoin and OnLeave callbacks aren't being triggered after calling SubscribeToChannelsAsync and UnsubscribeFromChannelsAsync

I have a single ServerEventsClient object that I use to dynamically subscribe and unsubscribe from channels as needed. I have some channels that are always open and that I pass in the constructor. I register to other channels by calling SubscribeToChannelsAsync(). The connection is actually established and I am able to communicate with the other side using it (I'm using SSE as chat), but none of our OnJoin registered methods get called. The same is true for UnsubscribeFromChannelsAsync() and OnLeave. I tried using the UpdateSubscriberAsync() and got the same results.
Worth noting is the fact that I have NotifyChannelOfSubscriptions set to true in my ServerEventsFeature.
Could the problem be in the fact that we are (un)subscribing after we initialize the ServerEventsClient object with initial channels?
When a subscribers channels subscription is updated after they've subscribed it fires an onUpdate event.

How to persist Saga instances using storage engines and avoid race condition

I tried persisting Saga Instances using RedisSagaRepository; I wanted to run Saga in load balancing setup, so I cannot use InMemorySagaRepository.
However, after I switched, I noticed that some of the events published by Consumers were not getting processed by Saga. I checked the queue and did not see any messages.
What I noticed is it will likely occurs when the Consumer took little to no time to process command and publish event.
This issue will not occur if I use InMemorySagaRepository or add Task.Delay() in Consumer.Consume()
Am I using it incorrectly?
Also, If I want to run Saga in load balancing setup, and if the Saga needs to send multiple commands of the same type using dictionary to track completeness (similar logic as in Handling transition to state for multiple events). When multiple Consumer publish events at the same time, would I have race condition if two Sagas are process two different events at the same time? In this case, would the Dictionary in State object will be set correctly?
The code is available here
SagaService.ConfigureSagaEndPoint() is where I switch between InMemorySagaRepository and RedisSagaRepository
private void ConfigureSagaEndPoint(IRabbitMqReceiveEndpointConfigurator endpointConfigurator)
{
var stateMachine = new MySagaStateMachine();
try
{
var redisConnectionString = "192.168.99.100:6379";
var redis = ConnectionMultiplexer.Connect(redisConnectionString);
///If we switch to RedisSagaRepository and Consumer publish its response too quick,
///It seems like the consumer published event reached Saga instance before the state is updated
///When it happened, Saga will not process the response event because it is not in the "Processing" state
//var repository = new RedisSagaRepository<SagaState>(() => redis.GetDatabase());
var repository = new InMemorySagaRepository<SagaState>();
endpointConfigurator.StateMachineSaga(stateMachine, repository);
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
LeafConsumer.Consume is where we add the Task.Delay()
public class LeafConsumer : IConsumer<IConsumerRequest>
{
public async Task Consume(ConsumeContext<IConsumerRequest> context)
{
///If MySaga project is using RedisSagaRepository, uncomment await Task.Delay() below
///Otherwise, it seems that the Publish message from Consumer will not be processed
///If using InMemorySagaRepository, code will work without needing Task.Delay
///Maybe I am doing something wrong here with these projects
///Or in real life, we probably have code in Consumer that will take a few milliseconds to complete
///However, we cannot predict latency between Saga and Redis
//await Task.Delay(1000);
Console.WriteLine($"Consuming CorrelationId = {context.Message.CorrelationId}");
await context.Publish<IConsumerProcessed>(new
{
context.Message.CorrelationId,
});
}
}
When you have events published in this manner, and are using multiple service instances with a non-transactional saga repository (such as Redis), you need to design your saga such that a unique identifier is used and enforced by Redis. This prevents multiple instances of the same saga from being created.
You also need to accept the events in more than the "expected" state. For instance, expecting to receive a Start, which puts the saga into a processing state, before receiving another event only in processing, is likely to fail. Allowing the saga to be started (Initially, in Automatonymous) by any of the sequence of events is recommended, to avoid out-of-order message delivery issues. As long as the events all move the dial from the left to the right, the eventual state will be reached. If an earlier event is received after a later event, it shouldn't move the state backwards (or to the left, in this example) but only add information to the saga instance and leave it at the later state.
If two events are processed on separate service instances, they'll both try to insert the saga instance to Redis, which will fail as a duplicate. The message should then retry (add UseMessageRetry() to your receive endpoint), which would then pick up the now existing saga instance and apply the event.

Azure WebJobs getting initialized randomly

We have webjobs consisting of several methods in a single Functions.cs file. They have servicebus triggers on topic/queues. Hence, keep listening to topic/queue for brokeredMessage. As soon as the message arrives, we have a processing logic that does lot of stuff. But, we find sometimes, all the webjobs get reinitialized suddenly. I found few articles on the website which says webjobs do get initialized and it is usual.
But, not sure if that is the only way and can we prevent it from getting reinitialized as we call brokeredMessage.Complete as soon we get brokeredMessage since we do not want it to be keep processing again and again?
Also, we have few webjobs in one app service and few webjobs in other app service. And, we find all of the webjobs from both the app service get re initialized at the same time. Not sure, why?
You should design your process to be able to deal with occasional disconnects and failures, since this is a "feature" or applications living in the cloud.
Use a transaction to manage the critical area of your code.
Pseudo/commented code below, and a link to the Microsoft documentation is here.
var msg = receiver.Receive();
using (scope = new TransactionScope())
{
// Do whatever work is required
// Starting with computation and business logic.
// Finishing with any persistence or new message generation,
// giving your application the best change of success.
// Keep in mind that all BrokeredMessage operations are enrolled in
// the transaction. They will all succeed or fail.
// If you have multiple data stores to update, you can use brokered messages
// to send new individual messages to do the operation on each store,
// giving eventual consistency.
msg.Complete(); // mark the message as done
scope.Complete(); // declare the transaction done
}

Rebus - Send delayed message to another queue (Azure ServiceBus)

I have a website and and a webjob, where the website is a oneway client and the webjob is worker.
I use the Azure ServiceBus transport for the queue.
I get the following error:
InvalidOperationException: Cannot use ourselves as timeout manager
because we're a one-way client
when I try to send Bus.Defer from the website bus.
Since Azure Servicebus have built in support for timeoutmanager should not this work event from a oneway client?
The documentation on Bus.Defer says: Defers the delivery of the message by attaching a header to it and delivering it to the configured timeout manager endpoint
/// (defaults to be ourselves). When the time is right, the deferred message is returned to the address indicated by the header."
Could I fix this by setting the ReturnAddress like this:
headers.Add(Rebus.Messages.Headers.ReturnAddress, "webjob-worker");
Could I fix this by setting the ReturnAddress like this: headers.Add(Rebus.Messages.Headers.ReturnAddress, "webjob-worker");
Yes :)
The problem is this: When you await bus.Defer a message with Rebus, it defaults to return the message to the input queue of the sender.
When you're a one-way client, you don't have an input queue, and thus there is no way for you to receive the message after the timeout has elapsed.
Setting the return address fixes this, although I admit the solution does not exactly reek of elegance. A nicer API would be if Rebus had a Defer method on its routing API, which could be called like this:
var routingApi = bus.Advanced.Routing;
await routingApi.Defer(recipient, TimeSpan.FromSeconds(10), message);
but unfortunately it does not have that method at the moment.
To sum it up: Yes, setting the return address explicitly on the deferred message makes a one-way client capable of deferring messages.

Resources