I know it is a time critical system ... employees need the information
ASAP... but what I do not understand is ...
- where is the application running ... on a server ... on a users work
station?
- who will be responsible for executing / sending messages ... one computer
or many different computers?
- will this message sender reside on a server / database server / or a
user's workstation ... ???
- what database are you using ???
- is this functionality wrapped up in a large application?
- why 4 threads ?
- how does you program determine which message to send ... parameter list
please.
------------------------------------
If this information is so mission critical, and employees must get their
message ASAP ... put a trigger on the database table.
... wrap the necessary functionality is a small stand alone exe application
...
... build your application so it receives commandline parameters / string
...
... build a trigger on the database .. fire on inserts ... have it call you
message program with a command parameter (messageID) ...
... install you application on the database server .... this will speed up
the connection / retrievals and so on ... no network latency.
Each time a message is inserted in the table, the trigger fires, calling
your EXE with the appropriate parameter string, exe starts, fires off
message ... done. Small exe can run as many times on the server...
------------------------------------
If you are using a timestamp for determining which messages to send ...
incorporate another table ... LastTimeExecuted ...
table: MessengerExecution
field: LastDateTimeFired
begin transactions ... lock table.
select lastdatetimefired from this table...in a variable
update field with thread date/time ...
end transaction.
return the select value and the update value
select messages where date is between lastdatetimefired and the
updateddatetime I just used...
------------------------------------
Again, there are many solutions ... just do not completely understand what
you are doing...
------------------------------------
Bottom line ...
- you have determined that you need to run a mutli-threaded process for
this.
- so, in order to avoid DUPLICATE MESSAGES .... you either have to
employee...
Transaction and Database Locking - look at isolation levels / settings
or
A booker type of system...
------------------------------------
have the broker continually pool the table ... this will only work it 1
machine is designated for messaging! If more than one machine will be used
for messaging ... you will have to roll up your sleeves and look at
Transactions and Isolation Levels.
CheckMessage
Do Until Company.Revenues < 0
MessegeID = broker.getNextMessage()
broker.sendMessage(MessageID)
Loop
SendMessage(MessageID)
Create a new thread...
SendMessage(MessageID) in this new thread...
Return to the MessageBroker.CheckMessage...while the other thread is
preparing and sending the message...
This will continuious poll the database server for new messages! ...
------------------------------------
However, if it is mission criticial users receive this information ASAP ...
TRIGGER on database table! If the user can wait 10 seconds .. build a
BROKER ... and have it spawn as many threads needed to send the messages in
the QUEUE ... have it control the process...and sending!
------------------------------------
Again, many solutions, depends on your needs ... code sample, table
structure ... some thing to trying and figure out how exactly your 'threads'
are getting 1 message...
Jeff.
Thanks Jeff, answers under your questions...
> I know it is a time critical system ... employees need the information
> ASAP... but what I do not understand is ...
>
> - where is the application running ... on a server ... on a users work
> station?
Server (2003)
> - who will be responsible for executing / sending messages ... one
> computer or many different computers?
One computer. One multithreaded vb.net app.
> - will this message sender reside on a server / database server / or a
> user's workstation ... ???
Remote SQL Server 2005 (EE)
> - what database are you using ???
SQL Server 2005 EE
> - is this functionality wrapped up in a large application?
Not very large. The logic around this loop cycle is pretty much the entire
application.
> - why 4 threads ?
Allows sending 4 messages at a time.
> - how does you program determine which message to send ... parameter list
> please.
Read from a table in the db server. A Queue table. (employeeid,
messageaddress)
> ------------------------------------
> If this information is so mission critical, and employees must get their
> message ASAP ... put a trigger on the database table.
Cant have a trigger execute a function in a remote vb.net app.
> ... wrap the necessary functionality is a small stand alone exe
> application
Exactly what I need to do.
> ...
> ... build your application so it receives commandline parameters / string
Considering it.
> ...
> ... build a trigger on the database .. fire on inserts ... have it call
[quoted text clipped - 283 lines]
>>>>>>>>
>>>>>>>> Jay
Chris Dunaway - 28 Nov 2006 21:47 GMT
> >>>>>>>>I have a multi threaded VB.NET application (4 threads) that I use to
> >>>>>>>>send text messages to many, many employees via system.timer at a 5
[quoted text clipped - 9 lines]
> >>>>>>>>use... problem is all 4 run fast enough to all use/mark the row.
> >>>>>>>>Any thoughts?
Why not have just one thread that reads the records from the db and
creates "TextMessage" objects that are inserted into a synchronized
queue. Then each of your four sending threads can all get their values
from this queue. Since the queue is synchronized, none of the 4
threads will be able to get the same TextMessage object from the queue.
The TextMessage object I am referring to is one you would create
yourself to encapsulate the data from a row in your queue table. The
Queue class in System.Collections (or the generic one in
System.Collections.Generic) have a SyncRoot property that can be used
to lock the queue when a thread is retrieving an object from it.
The method might look like this (theQueue is defined elsewhere):
Public Function GetMessage() As TextMessage
Dim obj As TextMessage
'SyncLock prevents other threads from dequeing the same object.
SyncLock theQueue.SyncRoot
If theQueue.Count > 0 Then
obj = theQueue.Dequeue()
End If
End SyncLock
Return obj
End Function
Hope this helps,
Chris
Cor Ligthert [MVP] - 29 Nov 2006 04:12 GMT
Jay,
The threads will never sent the messages at the same time.
In contrary it will need more time to use threads in an application like you
tell.
There is in no way a pause involved where another thread can use the
hardware.
Just to add what the others have already told more times to you.
Cor
> Thanks Jeff, answers under your questions...
>
[quoted text clipped - 339 lines]
>>>>>>>>>
>>>>>>>>> Jay
Stephany Young - 29 Nov 2006 12:32 GMT
Ok. Just in case you're interested, here's how we did something that sounds
very very similar to what you are trying to achieve.
Our requirement was to have a central message lodgement resource that had to
be able to handle up to 10,000 messages during a 9 hour working day. A given
message would be for 1 and only one user but we had to cater for up to 200
users.
Messages were automatically 'lodged' by an application that could be run by
any user in the system.
Any given user may not logged in at any given time, but any given user could
be logged into multiple computers simultaneously. This meant that to deliver
a message to a given user we had to to deliver it to all machines they were
logged into. If a user was not logged, we, of course, could not deliver a
message to them but it had to be delivered to them at the first avaible
opportunity.
The way we approached this was to have a client application that ran in the
users start menu. The application, when it started, 'registered' the user to
the message lodgement resource along with the machine name. When the
application terminated, (i.e. when the user logged off), the application
'de-registered' the user/machine combination.
The user also lodged messages for other users via this application
(including users who were not online).
The application also set up a TCP Listener on a predefined port for
receiving messages.
The message lodgement resource, comprised a SQL Server 2005 database and a
server application, both of which ran on the same box.
One part of the server application was dedicated to receiving messages from
the client applications and 'lodging' them in the database.
Another part of the server application was dedicated to retrieving messages
from the databse and distributing them to the clients.
A third part of the server application was dedicated to displaying
throughput and status information.
The database comprised 3 main tables. One table held static user
information, a second table held dynamic user/machine information and the
third table was the message store.
The structure of the message store table was, essentially:
create table messagestore(
ms_id bigint not null identity(1,1),
ms_timestamp datetime not null,
ms_sender nvarchar(50) not null,
ms_recipient nvarchar(50) not null,
ms_message nvarchar(max) not null,
ms_status int not null constraint df_messagestore_01 default (0),
constraint pk_messagestore primary key (ms_timestamp,ms_id)
)
The ms_id column was used purely to keep messages in the sequence that they
were lodged if the value for the ms_timestamp column was not unique.
When a message was lodged, a value for ms_status was NOT specified in the
insert statement therefore it started 'life' with a status of 0 whaich meant
that it was available for sending.
The retrieval of messages was done via a stored procedure as follows:
create procedure retrievemessage as
set nocount on
declare @id bigint
begin transaction
select top 1 @id=ms_id from messagestore where status=0 order by
ms_timestamp,ms_id
update messagestore set ms_status=1 where ms_id=@id
select
ms_id,,
ms_timestamp,
ms_sender,
ms_recipient,
ms_message
from
messagestore
where
ms_id=@id
commit transaction
go
The server transaction had a thread with a loop that continually exectued
that stored procedure via a datareader. If there was no row returned then
the thread slept for 50 milliseconds before continuing the loop. If a row
was returned, the thread dealt with the row and continued the loop
immediately. If there were 10 iterations of the loop without there being a
50 ms sleep then a 50 ms sleep was forced. This allowed the thread to 'do
its stuff' on a timely basis with out becoming unresponsive to any control
'commands'.
The prime intent was to send all available messages in the message store as
quickly as possible after they were lodged.
The thread spawned very short-lived worker threads to do the actual sending
passing the necessary values as parameters.
A single instance of a worker thread handled one and only one message.
The worker thread determined if the user was logged in. If so it sent a copy
of the message to every machine that the user was logged in to. If it
sucessfully sent the message it updated the ms_status value in the
appropriate database row from 1 to 2. If it was unable to send the message
for any reason it updated the ms_status value in the appropriate database
row from 1 to 3.
When we stress tested this we had various users logged into a toal of 20
machines and we preloaded the message store with 50,000 messages, (5 days
worth), for those user but about 5,000 messages were for users who were not
logged in. Two of the users were logged into 5 machines each. We then
started the retrieval thread.
The time it took that thread to deal with all 50,000 messages was just over
24 seconds. Of all the worker thread instances, the longest lived thread was
just over 300 milliseconds, and, as expected, that was one ofthe threads
handle a message for one of the users that was logged in to 5 machines. The
average length of the ms_message value was about 200 Kilobytes.
Certainly the hardware we were dealing with was reasonably beasty but we
were more than happy with the performance of the techniques we used, (we had
not expected it to be quite that good).
So the moral of the story is: Don't get all bitter and twisted about worst
case performance expectations. Give it a suck and see and you may find that
your fears about performance issues might be groundless.
Good luck!!!!!
> >>>>>>>>I have a multi threaded VB.NET application (4 threads) that I use
> >>>>>>>>to
[quoted text clipped - 14 lines]
> >>>>>>>>use... problem is all 4 run fast enough to all use/mark the row.
> >>>>>>>>Any thoughts?
> Thanks Jeff, answers under your questions...
>
[quoted text clipped - 339 lines]
>>>>>>>>>
>>>>>>>>> Jay
jeff - 29 Nov 2006 15:11 GMT
Jay,
> Cant have a trigger execute a function in a remote vb.net app.
Install the application on you DATABASE SERVER- make it a local APP... If
the messages are that important ... this will give you the quickest result!
A function CAN call a local exe ... I do it all the time - for broadcasting
status changes and sending critical alerts in an Patient Tracking system for
a hospital emergancy department...works like a hot dam ... even sends
messages to peoples pagers! If you DBA screams and yells and jumps up and
down - this is a database server, we are not install you little program on
it ... find out the persons name at the bottom of the DBA's paycheck, go to
them, tell them what is going on ... tell them what you are doing ... tell
them how critical this application / process is to organization - MISSION
CRITICAL ... tell them how much this program will impact the overall
performance of the database server ... sell it to them - the persons whos
name is at the bottom of the paycheck!
Again, if this is so mission critical, you must capture it at the moment the
data is made available...and you need fall-over, redundancy in you messaging
application.
what happens ... message in table ... 3 more seconds til the messaging
thread is fired ... 2 ... 1 ... oops, messaging machine is off-line! No
message sent! Now what? If on the database server ... if the data is saved,
the message is sent ... and if it is not sent ... alerts are sent... there
is a fail proof plan in place when messages are not SENT simply because the
system knows a message was suppose to be sent! However, if you are sending
messages for a machine that is off line and not polling for new messages
(because some network guy re-started the box and for got to restart your
process) ... there is now way of detecting unsent messages.
----------------------------------------------
What I do not understand ... is your reason to use '4' threads??? I know
speed ... mission critical . But why 4 threads??? Why not have a thread
for EACH MESSAGE ... 16 messages .... 16 threads! Using your reasoning,
would this not be even quicker!
----------------------------------------------
What I am saying is ... and have said before ...and is being repeated by
Chris ... he has taken my suggestion one step further by giving you useable
code... is you approach to multi threading the entire process is flawed ...
I am not argueing the need for multithreading in the process ... I am just
disagreeing with the need to multithread the entire process!
Example ... in plain text ... you figure out the code ...
1. Scenerio: Retrieve list of messages from the database... MessageBroker
... have MessageBroker create a seperate thread for each message it has to
send!
So, proces is...
- Get list of message from database ...
- Loop through list ...
- for each record ... call a SendMessage method ... include all the
necessary information (parameters) for selecting or sending the message
- Have the SendMessage start a new thread ... send the message information
to this new thread ... so its know which message to send ... have this new
thread send the message.
- while this thread is processing (sending the message) ... return to your
list of messages in the messagebroker ...
- messagebroker gets next message ... messagebroker calls sendmessage ...
sendmessage creates a new thread ... sendmessage send message in new thread,
while program returns to the loop ...
Do you see what I am doing here ... the prgram will create a seperate thread
for EACH message. The messagebroker is your control, it will ensure you do
not send duplicate messages...
This will only work, if you have a single machine designated as your
MessageBroker!
2. Scenerio - Use your 4 threads ...
x = 1 to 4
- Thread x retrieves a message from the database ...
- Thread x prepares message to send ... before it sends ... it checks the
database ... to see if any other thread has sent the message ...
YES another thread has sent it ... Thread x , drops message, and gets
another message ...
NO, nobody has sent, thread x locks the message row in the database
(transactions and isololation levels ... have you looked at these yet),
updates the sending flagging ... sends the message ... updates the sent
flag...
- Thread x gets another message ...
This process ... will create a lot of wasted CHECKING and UPDATING and
DATABASE LOCKING to ensure messages are not duplicated between recipients.
This is not faster!
By having 4 threads working independant of each other, you are slowing down
the process! and adding 'more threads', you could be compound the issue!
You need some mechanism for communicating between your threads ...
- an exe ... the messagebroker approach ... telling the thread which message
to send.
- the database ... implement row level locking ... look at Isolation and
Transactions ... to ensure each message row in only processed by 1 thread...
- hard code it ... implement a design structure - add the ThreadToProcess
field - in to your solution. This will cause maintenance and scalability
issues later in life! And is poor design...and a poor work-around!
So, Jay, I have tried to shine some light on the problem for you, it is up
to you to decide your next steps ... if this is so mission critical, and you
are having these difficults, maybe consultant out for a solution :-)
Jeff.
> Thanks Jeff, answers under your questions...
>
[quoted text clipped - 339 lines]
>>>>>>>>>
>>>>>>>>> Jay
Jay - 30 Nov 2006 19:21 GMT
Thanks a lot everyone... I'l re-read all messages and work this out. I
appreciate all the help and assistance. I'm sure with your suggestions
everything with get resolved. Thanks a lot.
> Jay,
>
[quoted text clipped - 464 lines]
>>>>>>>>>>
>>>>>>>>>> Jay