Multiple conversations on Service Broker - sql

Let say I have two instances of the same app interacting with a backend service in Service Broker. How can each instance know to handle only conversations it initiated and ignore the rest? If I recall correctly, every RECEIVE will remove the message from the queue.
Here's an example:
-- Assume the SquareService return the square of the number sent to it
-- Instance 1
BEGIN DIALOG #Conversation1
FROM SERVICE InitService
TO SERVICE 'SquareService'
ON CONTRACT (MyContract)
WITH ENCRYPTION = OFF;
SEND ON CONVERSATION #Conversation1 MESSAGE TYPE MyMessageType('1');
-- Instance 2
BEGIN DIALOG #Conversation2
...;
SEND ON CONVERSATION #Conversation2 MESSAGE TYPE MyMessageType('2');
Now who should I write the RECEIVE statement so that Instance 1 will correctly get 1 and Instance 2 get 4 back?

You are already using a Conversation Group.
Is this not sufficient for your needs when Receiving the messages?
-> using GET CONVERSATION GROUP and RECEIVE together
you can read more about it here: http://technet.microsoft.com/en-us/library/ms166131%28v=sql.105%29.aspx
and also here Sql Server Service Broker Conversation Groups

I'm assuming you have an InitQueue associated with your InitService. You can use a WHERE clause with RECEIVE to listen for messages on the same conversation:
WAITFOR (RECEIVE #response = CONVERT(xml, message_body)
FROM InitQueue -- InitService setup to use InitQueue?
WHERE conversation_handle = #Conversation1

Related

How to set routing key for producer and consumer successfully

I'm building a SpringCloud Stream based application and exchange type is topic and message is sent to 2 queue consumer groups from the topic exchange. The scenario is something like this:
Service A in my application wants to send message of type appointments to service B and service C via an exchange named as: appointments-request based on different use case scenarios such as book, cancel, update etc.
So messages with a key appointments.book.B or appointments.cancel.B should go to consumer queue group appointments.B
messages with a key appointments.book.C or appointments.cancel.C should go to consumer queue group appointments.C
How to achieve this successfully?
Configuration of Producer Service:
spring.cloud.stream.bindings.output.destination=appointments-request
spring.cloud.stream.bindings.input.destination=appointments-reply
spring.cloud.stream.rabbit.bindings.output.producer.exchangeType=topic
spring.cloud.stream.rabbit.bindings.output.producer.routingKeyExpression=
appointments.#.#
Configuration of Consumer Service B:
spring.cloud.stream.rabbit.bindings.input.consumer.exchangeType=direct
spring.cloud.stream.rabbit.bindings.input.consumer.group=
appointments.docmgmt
spring.cloud.stream.rabbit.bindings.input.consumer.bindingRoutingKey=
appointments.docmgmt
spring.cloud.stream.rabbit.bindings.input.consumer.routingKeyExpression=
appointments.#.docmgmt
Producer Service A has the below method to set routing key
public boolean send(AppointmentEvent appointmentEvent)
{
logger.info("Sending event {} ",appointmentEvent);
return this.source.output().
send(MessageBuilder.withPayload(appointmentEvent).
setHeader(SimpMessageHeaderAccessor.DESTINATION_HEADER,
"appointments.book.docmgmt").build());
}
My communication between services is not working.
appointments.#.#
You can't use wildcards on the producer side.
You need something like
spring.cloud.stream.rabbit.bindings.output.producer.routingKeyExpression=headers['routingKey']
And then the producer sets the routingKey header to the desired value for each message.
You shouldn't really use the Simp headers; that is for STOMP; use your own header.

ActiveMQ Durable Subscriber with jmsMessageId

I'm new to ActiveMQ and trying to find anything that explicitly outlines how JMSMessageID behaves with durable subscribers and selectors, however, I am struggling to find much.
As an example: JMSType = 'car' AND color = 'blue' AND weight > 2500 as a selector. Each subscriber will only receive messages from the topic where the criteria match. When each receives said messages are the JSMMessageID unique for each subscriber or are they unique for the entire topic before it was filtered by the selector for the subscriber.
If not is there a way that I can get the JSMessageID to be unique for each subscriber so that it can be used as a form of sequence number using custom messageID layout: 1, 2, 3... ad infinitum.
The Message ID is set by the producer at the time of send, the broker passes along a copy of the message to each Topic subscription (durable or not) with the message ID that it was sent with. You cannot alter the ID the broker uses that value to track the message and ensure that it is preserved until each subscription that it was dispatched to or stored for has acknowledged it.

Can a telegram bot block a specific user?

I have a telegram bot that for any received message runs a program in the server and sends its result back. But there is a problem! If a user sends too many messages to my bot(spamming), it will make server so busy!
Is there any way to block the people whom send more than 5 messages in a second and don't receive their messages anymore? (using telegram api!!)
Firstly I have to say that Telegram Bot API does not have such a capability itself, Therefore you will need to implement it on your own and all you need to do is:
Count the number of the messages that a user sends within a second which won't be so easy without having a database. But if you have a database with a table called Black_List and save all the messages with their sent-time in another table, you'll be able to count the number of messages sent via one specific ChatID in a pre-defined time period(In your case; 1 second) and check if the count is bigger than 5 or not, if the answer was YES you can insert that ChatID to the Black_List table.
Every time the bot receives a message it must run a database query to see that the sender's chatID exists in the Black_List table or not. If it exists it should continue its own job and ignore the message(Or even it can send an alert to the user saying: "You're blocked." which I think can be time consuming).
Note that as I know the current telegram bot API doesn't have the feature to stop receiving messages but as I mentioned above you can ignore the messages from spammers.
In order to save time, You should avoid making a database connection
every time the bot receives an update(message), instead you can load
the ChatIDs that exist in the Black_List to a DataSet and update the
DataSet right after the insertion of a new spammer ChatID to the
Black_List table. This way the number of the queries will reduce
noticeably.
I have achieved it by this mean:
# Using the ttlcache to set a time-limited dict. you can adjust the ttl.
ttl_cache = cachetools.TTLCache(maxsize=128, ttl=60)
def check_user_msg_frequency(message):
print(ttl_cache)
msg_cnt = ttl_cache[message.from_user.id]
if msg_cnt > 3:
now = datetime.now()
until = now + timedelta(seconds=60*10)
bot.restrict_chat_member(message.chat.id, message.from_user.id, until_date=until)
def set_user_msg_frequency(message):
if not ttl_cache.get(message.from_user.id):
ttl_cache[message.from_user.id] = 1
else:
ttl_cache[message.from_user.id] += 1
With these to functions above, you can record how many messages sent by any user in the period. If a user's messages sent more than expected, he would be restricted.
Then, every handler you called should call these two functions:
#bot.message_handler(commands=['start', 'help'])
def handle_start_help(message):
set_user_msg_frequency(message)
check_user_msg_frequency(message)
I'm using pyTelegramBotAPI this module to handle.
I know I'm late to the party, but here is another simple solution that doesn't use a Db:
Create a ConversationState class to attach to each telegram Id when they start to chat with the bot
Then add a LastMessage DateTime variable to the ConversationState class
Now every time you receive a message check if enought time has passed from the LasteMessage DateTime, if not enought time has passed answer with a warning message.
You can also implement a timer that deletes the conversation state class if you are worried about performance.

Unique ID outside of Message Body in Service Broker

We are trying to implement a system that look like this diagram:
In step 1, the User Application will call a Stored Procedure to call the service (begin the dialog conversation). And we need an ID to identify that request.
After that, the User Application will start making periodical request to the Reply "Queue" using Receive statement with Where clause (I put Queue in the double quote, because in fact it doesn't really work like a queue).
Here, the problem we have is that, how can we maintain a unique ID throughout the process (step 2 and 3), and that ID is somewhere outside of Message Body, because we want to use Where clause of Receive. The conversation_handle could not help us, because they are different from each endpoint.
Is there any ID that persist from the begin of the dialog until END CONVERSATION?
"Periodically ask for a response for a specific request"
You cannot do that. Your application must dequeue responses and handle them, as they become available, all of them. Any architecture that attempts to poll for specific messages will fail. This is a fundamental tenet of Service Broker.
Also you do not "ask periodically" in Service Broker. You have WAITFOR (RECEIVE ...) exactly to replace pooling 'periodically'.
We use this syntax:
BEGIN DIALOG CONVERSATION #Handle
FROM SERVICE #InitiatorService
TO SERVICE #TargetService
ON CONTRACT #OnContract
WITH ENCRYPTION = OFF;
The variable #Handle will after this statement contain a uniqueidentifier which will remain the same during the entire conversation. On the receiving end, we have this:
RECEIVE TOP(1)
#Handle = [conversation_handle],
#Type = [message_type_name],
#Message = [message_body]
FROM
[TargetQueue]
Which get the same value for #Handle as was created by the sender.
We have pretty much just implemented this: http://rusanu.com/2007/12/03/resending-messages/ and it works great.

Cannot send message to SQL Server 2008 Service Broker: The message type '<message type>' is not part of the service contract

I'm trying to learn the basics of Service Broker, and have created an application originally based on the SSMS template. However I can't send a message to my queue. It just says the message type is not part of the service contract.
The absolute bare minimum I need to recreate this is the following batch:
USE [test_db]
GO
CREATE MESSAGE TYPE [test_message]
AUTHORIZATION [dbo]
VALIDATION = WELL_FORMED_XML
GO
CREATE CONTRACT [test_contract]
AUTHORIZATION [dbo] (
[test_message] SENT BY ANY
)
GO
CREATE QUEUE [dbo].[test_queue]
WITH STATUS = ON
,RETENTION = OFF
--,ACTIVATION (
-- STATUS = ON
-- ,PROCEDURE_NAME = [dbo].[test_activator]
-- ,MAX_QUEUE_READERS = 1
-- ,EXECUTE AS N'dbo'
--)
ON [PRIMARY]
GO
CREATE SERVICE [test_service]
AUTHORIZATION [dbo]
ON QUEUE [dbo].[test_queue] (
[test_contract]
)
GO
BEGIN TRANSACTION
DECLARE #dialog_handle UNIQUEIDENTIFIER
BEGIN DIALOG #dialog_handle
FROM SERVICE test_service
TO SERVICE N'test_service';
SEND ON CONVERSATION #dialog_handle
MESSAGE TYPE test_message (N'<test />');
END CONVERSATION #dialog_handle;
COMMIT TRANSACTION
GO
...which yields:
Msg 8431, Level 16, State 1, Line 10
The message type 'test_message' is not part of the service contract.
I only need to be able to send asynchronous messages within the same database. There are no remote connections to consider, and I don't strictly even need to handle a reply.
I've checked and double-checked that the message type, contract, queue and service all exist and the properties of the contract says that the message type is included
What am I missing?
As Denis already answered, you're missing the ON CONTRACT test_contract.
If you omit it then the DEFAULT contract is used. Every database has a contract named DEFAULT which has one message type, also named DEFAULT. The DEFAULT contract is used when you omit any contract in BEGIN DIALOG and the DEFAULT message type is used when you omit the message type in SEND:
BEGIN DIALOG #dialog_handle
FROM SERVICE test_service
TO SERVICE N'test_service'; <-- will use the DEFAULT contract
SEND ON CONVERSATION #dialog_handle
(N'<test />'); <-- will use the DEFAULT message type
DEFAULT message type has no validation. The DEFAULT contract binds the DEFAULT message type to both initiator and target (both can send the message in this contract, ie. SENT BY ANY). Contract and message type names are always case sensitive, irrelevant of database collation, so the name DEFAULT is case sensitive.
Try this:
BEGIN DIALOG #dialog_handle
FROM SERVICE test_service
TO SERVICE N'test_service'
ON CONTRACT test_contract;