?
Solved

Real-time data service - client to service (on timer) - service to data (on timer)

Posted on 2012-08-30
23
Medium Priority
?
477 Views
Last Modified: 2012-09-11
I need to create a service which runs specific queries against a database every 30 seconds and then stores some aggregates into some tables. Each client will have a browser interface that will call this same service to get data from this service's tables every 20 seconds.  

The reason for this design is so that every client doesn't cause a data access to repeat itself and add to unnecessary processing.

Any ideas on design considerations? Should one service handle it all?
0
Comment
Question by:petel2k
  • 12
  • 11
23 Comments
 
LVL 9

Assisted Solution

by:Orcbighter
Orcbighter earned 2000 total points
ID: 38353319
First thing would be to separate these two functions into separate services.
The first service would run against the database and update the aggregate tables.
The second service, run from the client's browser will run at a given time period (every 20 seconds) and retrieve information from these data tables.
0
 

Author Comment

by:petel2k
ID: 38361502
Problem is that it's an IVR that makes the call to the first service. It must disconnect to the service to play a recording. Without the disconnect, the caller would hear silence, not good. However meanwhile the recording is playing to the customer, I need the row I dropped off in a table of the firest service, which has account #,  to be processed. I need an event to make this happen without the first service, because it's ended. This way, when the caller message is completed, the IVR can call another service to pick up the information retrieved. (It's the look up that takes a while and I have no control over it, I need to have this hgappen on its own)
0
 
LVL 9

Assisted Solution

by:Orcbighter
Orcbighter earned 2000 total points
ID: 38361977
By IVR, I assume you mean Interactive Voice Response, and that your clients are choosing options based on keypad choices.
It is not clear to me from your response (you talk about account #) whether your service is an open service (like Directory assistance - anyone can call in and use it), or is a subscription service (only subscribers with an account can use it).
In any case, my understanding is that the client/customer would make a call, choose some option and then hear a recorded message. You want this to be the latest version of the recorded message. Is this correct?
If so, my original answer still applies.
Your client/customer rings a number and gets the latest version of the chosen recorded message. If they miss the absolute latest one it does not matter because there will be a new one in 20 seconds or so.

I assume that this processing and storing of recored messages is occurring at some computer back-end, and not on the client's machine. In this case, I fail to see why the first service (the one actually updating the database) has any connection to the retrieval service used by the customer. This first service can be an automated process run on the backend and completely transparent to your customers.
Or have I missed something?
0
Transaction-level recovery for Oracle database

Veeam Explore for Oracle delivers low RTOs and RPOs with agentless transaction log backup and transaction-level recovery of Oracle databases. You can restore the database to a precise point in time, even to a specific transaction.

 

Author Comment

by:petel2k
ID: 38362015
Thanks for responding.

Yes, it is a voice interactive system. The first thing I ask for is the account number and then offer some choices. With the account number, I need to pull some data together. The problem is that it takes 3 seconds to get the data (out of my control), too long and would mean the caller would hear dead air for 3 seconds. Instead, once I get the account number, I'd like to store the account number to a table. While the message is playing, I'd do the look up for account info. Problem is that the IVR allows me to call services, but it must wait for a response before continuing with the next message. I'd like the first service to take the account number and store it to a table. While the message is playing I'd like another service to get the data and store it to the same table row. Once the message is over, another service would pick up the results. Reading and writing is fast, to a single table. It's the look up that needs to be fired with the original service going away. BTW, there are lots of calls and sometimes up to 50 in a minute. FIFO is important.

after getting the data, I can now provide information to caller, no matter what option they selected
0
 
LVL 9

Assisted Solution

by:Orcbighter
Orcbighter earned 2000 total points
ID: 38362045
Do you have programming control over both services? Or is the data collection service a third-party program?

Is the data you collect for a customer very different for each customer? Or is it one of a set of pre-set packages of data?
0
 

Author Comment

by:petel2k
ID: 38363255
The only control I don't have is over the service which gets the data.

1. Service which adds a row with account # (Mine)
2. Service that requests data from a corporate service and stores to results to table (mine)
3. Corporate service that actually gets the warehouse data (Called from #2 -I can only call it and receive results, no control)
4. Service that reads the table row with results (mine)
5. Database (Mine)


The corporate service looks up account info from many databases, from many corporate divisions, and returns in a common format..


Thanks
0
 
LVL 9

Assisted Solution

by:Orcbighter
Orcbighter earned 2000 total points
ID: 38363451
OK, Still a bit unclear on some things.
In your original question, you state

"Each client will have a browser interface that will call this same service to get data from this service's tables every 20 seconds."

But in your last response you state

"Yes, it is a voice interactive system. The first thing I ask for is the account number and then offer some choices."

So, do I assume that the actual voice interactive system will be an automated voice call made from behind the browser based on account (or login) information entered by the user on a browser-based form?
If so, can I assume that your intent is to keep making this IVR call from the client's session every 20 seconds that they are connected and that this will result in a pseudo-live updated text display on the browser?
What exactly will your client do to get the information (call by phone, or fill in web form)?
What exactly will the client see/hear on the phone/screen?
0
 

Author Comment

by:petel2k
ID: 38363543
There are no browser interfaces, rather its an IVR app which interacts with customers. Sorry if I'm not clear.

 Example of a real process

Caller dials company phone number and hears message "Using the touch keys on your phone, please enter your account #"

caller enters their account #

The IVR app (which can call a service and wait for a response) then calls my service which adds the account # to a table in a database (I have control over the DB and this service). The service must end fast, which it does. or the caller hears silence.

The caller next hears a message as "We have new products... blah blah blah. Please enter 1 for .. 2 for .. (note, while this is playing, the IVR can not call any services. It can only play this message)

I need a method to now retrieve the account # information from the row I added and to get name, main phone number, outstanding balance from an outside (corporate) service, but it takes too long to receive, 3 secs. So while the above message is playing, I'd like the look up (call to a corporate service to get data. I can only call and receive from this service, not my service) to happen and then add the information to the row in the database where I stored that customers account #.

After the message is played, the IVR will call another service (I have control over) which will read the updated row which now contains customer info.

The IVR only allows for synchronous processing. I'm trying to optimize on time without leaving the caller with moments of silence. Independent processes will enable that.

There are lots of calls and requests must occur in a FIFO. Would using MSMQ and adding a listener work the best? Using a db service broker work better? Or is there even a more efficient method?
0
 
LVL 9

Assisted Solution

by:Orcbighter
Orcbighter earned 2000 total points
ID: 38364008
From your descriptions it seems like you are designing for a landline telephone, or a simple mobile phone, and excluding smart phones. Yes?
0
 

Author Comment

by:petel2k
ID: 38364229
VOIP.
0
 
LVL 9

Assisted Solution

by:Orcbighter
Orcbighter earned 2000 total points
ID: 38371612
Just so I understand:
From your description, a customer will make a call via VOIP (only, or landline as well?) number to get information. There will be a sequence of events that occur but the customer will experience a single uninterrupted call.

The sequence of events are:
1. The number called will fire up your IVR app.

2. The customer will hear a recorded message asking him/her for their account number, which they enter via the touch keys (what about VOIP calls from a PC? no touch-tome on a PC keyboard)

3. Your IVR app calls your first service which adds the account number to a table in your database and then this service ends.

4. Your IVR app then plays a recorded message offering a number of menu options for the customer to choose from. When a selection is made you then call a third-party service to retrieve the relevant personal details of the customer, including name, phone, outstanding balance. A process that can take up to three seconds (or more?). The information returned from this service is captured by your app and added to the database table for that given account number.

5. Your app will now call your second service that retrieves the information from the database and plays it back to the customer.

6. The customer hangs up the phone, transaction complete.

=============================

Why does your app need to call an IVR service to store the account number? Why can't you just insert the number directly into the database? Or. if it is not on the same machine, make a non-IVR call to your database to add it?

Why can't your app make more than one IVR call at a time? I know they are synchronous, but can you not run more than one thread from your app? Each thread could handle its own synchronous call. In this way, you could call for balance information on one thread and retrieve the information to populate your database on another thread?
0
 

Author Comment

by:petel2k
ID: 38371849
The sequence of events are: ( Close, hear it is)

1. The number called will fire up your IVR app.

2. The customer will hear a recorded message asking him/her for their account number, which they enter via the touch keys (what about VOIP calls from a PC? no touch-tome on a PC keyboard)

3. Your IVR app calls your first service which adds the account number to a table in your database and then this service ends. Yes

4. Your IVR app then plays a recorded message offering a number of menu options for the customer to choose from. While this message is playing, a background process needs to retrieve data based on the account number added to the row in step 2 to avoid silence, i created a service to call the third party service When a selection is made you then call a third-party service (no longer needed since this was done in the background during the message play) to retrieve the relevant personal details of the customer, including name, phone, outstanding balance. From the results of the background process, which was added to that same row with account number we added earlier.   A process that can take up to three seconds (or more?). Yes, but now the customer does not need to wait during the silence as the data is looked up during the previous message.

5. Your app will now call your second service that retrieves the information from the database row  and plays it back to the customer.

6. The customer hangs up the phone, transaction complete.

Yes..

The IVR is not that capable. Before this, the customer would call, enter their account number and wait during a period of silence <-- bad.

The fix is to get the number, call a service from the IVR to drop it off (I've added MSMQ) and then return. This is super fast. While the message is playing, a windows service looks at the private queue (MSMQ) to see if any messages are there. If one is present, it passes it to another service which will perform the look up and add the customer data to a single row in the database for faster retrieval.

I've been advancing along and now I need to create an independent windows service to continuously check for messages, FIFO. This service must be able to read a message and fire an independent process.  What I don't want is the service to read a message, process it and then go to the next message. I need it to fire through the messages with multiple look ups happening at the same time. <-- this is where I need help now.
0
 

Author Comment

by:petel2k
ID: 38371854
BTW, thanks for your help..
0
 
LVL 9

Assisted Solution

by:Orcbighter
Orcbighter earned 2000 total points
ID: 38372147
OK, so basically you want help to design the Windows Service application?
What language are you using, C++, C#?

Firstly, you stated
This service must be able to read a message and fire an independent process.  What I don't want is the service to read a message, process it and then go to the next message. I need it to fire through the messages with multiple look ups happening at the same time.
We need to clear up some terminology here. By process, do you mean a complete independenty executable program, or do you mean an independent thread?
0
 

Author Comment

by:petel2k
ID: 38372592
Thanks again..

We need to clear up some terminology here. By process, do you mean a complete independenty executable program - Yes, or do you mean an independent thread.?

BTW, I'm looking at the sample VB service (I'm more familiar with VB than C# (do use C# on occasion. So either would work for me) see it here http://code.msdn.microsoft.com/windowsdesktop/VBWindowsService-3fc2805b

I built it and installed it on the server, I had no problems.

I presume the code I'd write would be in Private Sub ServiceWorkerThread(ByVal state As Object)

Given that, I'd like ServiceWorkerThread to read the MSMQ starting with the oldest entry (I know how to do this part) For each message read, I will fire a web service with the contents of the message (I have no problems writing web services ASMX ) However I do not want each message read and its web service request to hold up the other messages from being processed. This is where I need the help.

Using the example in the link, can I work this into something useful?

Thanks
0
 
LVL 9

Assisted Solution

by:Orcbighter
Orcbighter earned 2000 total points
ID: 38377315
First:
Yes, the Private Sub ServiceWorkerThread is where you would start your coding.
However, be mindful that this subroutine must be responsive to queries from the Windows Service Control Manager (SCM), otherwise the service will crash. This means it cannot become compute-bound or put into a sleep for long periods. If the service is stopped, it must exit, hence the code
    While  Not Me.stopping
        ...
        ...
    End While

Second: How to approach the subject.
I wrote a little console application to show proof of concept. I will append some code samples at the end.

The ServiceWorkerThread subroutine is the part of the service where your code will reside (or part of it), and is the part of the program that is started, stopped and/or paused by the SCM.

In this subroutine I would call the read method to get the next message off the MSMQ queue. If successful, I would add it to a list and then go back and wait for the next message on the queue. Quick, no delay, just what you asked for.
Now, as you are aware, the Read can be either synchronous or asynchronous. Unless you are going to deal with more than one queue at a time, I would stick with the synchronous call. Its easier and less complicated than dealing with completion ports etc.
However, you cannot be locked into a blocking call for too long in a Service or the SCM will regard it as having crashed. To this end, you place a timeout parameter in the call, say 100 milliseconds. If after testing you regard this as too small a timespan, you could extend the timeout to maybe 500 milliseconds.
This means that, when the program returns from a call to the Read it may be because a message was received, or because the timeout period had expired, or an error. If a message was received, good! Add it to the list. If a timeout, just loop around and wait on the Read again (this gives the SCM access to the thread. If an error, then you must decide whether to continue or exit, depending on your requirements.

How is this list of messages processed?
Firstly, you add a member variable to the Service class, which is a list of queue messages.
Since this list will be accessed by a number of threads, you will need to use a lock to make it thread safe.
In my example I create a wrapper class around the list, with public Add and Remove methods, so I can ensure the locks are used correctly.

Secondly, you add a subroutine to the service class, calling it, say, WaitForMessages().
What does it do? It loops around taking messages from the list and processing them. Since this is a separate thread it does not cause any delay in the thread that is reading from the queue.

Thirdly, in the ServiceWorkerThread subroutine, just before the While loop, you launch a thread with this subroutine as the thread function.

So, when the SCM starts the Service, the ServiceWorkerThread will be called. It will launch a separate thread with WaitForMessages as its thread function. The ServiceWorkerThread will then go into a loop reading any incoming messages from the queue and passing them to the list.

My little test program just deals with strings and, since I don't have MSMQ installed, I just artificially generate string in a loop, with a Sleep command to simulate waiting for an incoming message. It is good enough to show you the proof of concept though.
The wrapper class for the list:
Public Class ListHolder
    Private m_list As New List(Of String)
    Private m_lockObject As New Object

    Public ReadOnly Property Count() As Int32
        Get
            Count = m_list.Count
        End Get
    End Property

    Public Sub AddToList(ByVal item As String)
        SyncLock m_lockObject
            m_list.Add(item)
        End SyncLock
    End Sub

    Public Sub PopFromList(ByRef item As String)
        SyncLock m_lockObject
            item = m_list.First()
            m_list.RemoveAt(0)
        End SyncLock
    End Sub

End Class

Open in new window


Add this variable to the Service class
    Private m_list As New ListHolder

Open in new window

Now the ServiceWorkerThread subroutine:
        Dim myStr As String
        'Dim loopCounter As Integer = 0
        Dim stQueueName As String = "Private$queue"
        Dim que As MessageQueue
        Dim msg As Messaging.Message
        Dim binReader As BinaryReader

        Dim inObject As String = ""
        Dim bAll_OK As Boolean = True

        Console.WriteLine("Inside ServiceWorkerThread - {0}.", myStr)

        If (MessageQueue.Exists(stQueueName)) Then
            que = New MessageQueue(stQueueName)
        Else
            bAll_OK = False
            Console.WriteLine("ERROR: Unable to attach to Queue {0}", stQueueName)
        End If

        If (bAll_OK) Then
            Try
                ' now create the thread that will process the list of queue objects
                ThreadPool.QueueUserWorkItem(New WaitCallback(AddressOf WaitForMessages))

            Catch ex As Exception
                bAll_OK = False
                Console.WriteLine("ERROR: Unable to create thread to process list - {0}", ex.Message())
            End Try
        End If


        While  (bAll_OK And (Not Me.stopping)) 
        'While (bAll_OK And (loopCounter < 20))
            Try
                msg = que.Receive(New TimeSpan(0, 0, 0, 0, 100))   ' sleep for 100 milliseconds
                binReader = New BinaryReader(msg.BodyStream)

                ' read from the queue and add to the list
                inObject = New String(binReader.ReadChars(CInt(msg.BodyStream.Length)))
                'inObject = "This is a string message - " + loopCounter
                m_itemList.AddToList(inObject)

            Catch ex As MessageQueueException
                If (ex.MessageQueueErrorCode = MessageQueueErrorCode.IOTimeout) Then
                    ' if timeout ignore
                    Continue While
                Else
                    Console.WriteLine("ERROR: Unable to read message from queue: {0} - {1}", stQueueName, ex.Message())
                    ' choose here to either continue or exit on error 
                End If

            Catch ex As Exception
                bAll_OK = False
                Console.WriteLine("ERROR: Unexpected error reading from queue: {0} - {1}", stQueueName, ex.Message())
            End Try

            'Thread.Sleep(1000)               ' only sleeping here to simulate reading from queue with timeout
            'loopCounter = loopCounter + 1    'only for debugging, remove when testing if service is running
        End While
    End Sub

Open in new window


The WaitForMessages subroutine will has two loops. The outer loop will test to see if the Service has been stopped. The inner loop will pull any outstanding messages from the list and process them. When the list is empty it will sleep for 100 milliseconds and try again.

 Public Sub WaitForMessages()
        Dim myStr As String = ""

        Console.WriteLine("Inside WaitForMessages.")

        'While Not Me.stopping 
        While True
            While (m_itemList.Count > 0)
                ' get item from the list and process it
                m_itemList.PopFromList(myStr)
                ProcessMessages(myStr)
            End While

            Thread.Sleep(100)
        End While
    End Sub

Open in new window


In my example the ProcessMessages subroutine merely prints to the console, but yours would make information requests
 Public Sub ProcessMessages(ByVal msg As String)
        Console.WriteLine("ProcessMessages: {0}", msg)
        ' do processing stuff here
    End Sub

Open in new window

0
 

Author Comment

by:petel2k
ID: 38377690
Since I'm using the following code to get the message from MSMQ using queue.Receive which also removes the message immediately, I wouldn't need to worry of locking out the message from another thread. Is that correct? It also uses a 5 second timeout, should I remove the timeout?

Should I place this code in WaitForMessages() ?  Also,, I'll past the entire source in the next post.

            m = queue.Receive(New TimeSpan(0, 0, 5))
            m.Formatter = New XmlMessageFormatter(New String() {"System.String"})
            txMsg.Text = m.Body
            txEnum.Text = m.Label

Open in new window

0
 

Author Comment

by:petel2k
ID: 38377693
Here it is..  Thanks so much!  
Imports System.Messaging
Imports System.Threading


Public Class AspectMSMQlistener
    Private m_list As New ListHolder

    Public Sub New()
        InitializeComponent()

        Me.stopping = False
        Me.stoppedEvent = New ManualResetEvent(False)
    End Sub



    Protected Overrides Sub OnStart(ByVal args() As String)
        ' Log a service start message to the Application log.
        Me.EventLog1.WriteEntry("AspectMSMQWindowsService in OnStart.")

        ' Queue the main service function for execution in a worker thread.
        ThreadPool.QueueUserWorkItem(New WaitCallback(AddressOf ServiceWorkerThread))
    End Sub


    ''' <summary>
    ''' The method performs the main function of the service. It runs on a 
    ''' thread pool worker thread.
    ''' </summary>
    ''' <param name="state"></param>
    Private Sub ServiceWorkerThread(ByVal state As Object)
        Dim myStr As String
        'Dim loopCounter As Integer = 0
        Dim stQueueName As String = "Private$queue"
        Dim que As MessageQueue
        Dim msg As Messaging.Message
        Dim binReader As BinaryReader

        Dim inObject As String = ""
        Dim bAll_OK As Boolean = True

        Console.WriteLine("Inside ServiceWorkerThread - {0}.", myStr)

        If (MessageQueue.Exists(stQueueName)) Then
            que = New MessageQueue(stQueueName)
        Else
            bAll_OK = False
            Console.WriteLine("ERROR: Unable to attach to Queue {0}", stQueueName)
        End If

        If (bAll_OK) Then
            Try
                ' now create the thread that will process the list of queue objects
                ThreadPool.QueueUserWorkItem(New WaitCallback(AddressOf WaitForMessages))

            Catch ex As Exception
                bAll_OK = False
                Console.WriteLine("ERROR: Unable to create thread to process list - {0}", ex.Message())
            End Try
        End If


        While (bAll_OK And (Not Me.stopping))
            'While (bAll_OK And (loopCounter < 20))
            Try
                msg = que.Receive(New TimeSpan(0, 0, 0, 0, 100))   ' sleep for 100 milliseconds
                binReader = New BinaryReader(msg.BodyStream)

                ' read from the queue and add to the list
                inObject = New String(binReader.ReadChars(CInt(msg.BodyStream.Length)))
                'inObject = "This is a string message - " + loopCounter
                m_itemList.AddToList(inObject)

            Catch ex As MessageQueueException
                If (ex.MessageQueueErrorCode = MessageQueueErrorCode.IOTimeout) Then
                    ' if timeout ignore
                    Continue While
                Else
                    Console.WriteLine("ERROR: Unable to read message from queue: {0} - {1}", stQueueName, ex.Message())
                    ' choose here to either continue or exit on error 
                End If

            Catch ex As Exception
                bAll_OK = False
                Console.WriteLine("ERROR: Unexpected error reading from queue: {0} - {1}", stQueueName, ex.Message())
            End Try

            'Thread.Sleep(1000)               ' only sleeping here to simulate reading from queue with timeout
            'loopCounter = loopCounter + 1    'only for debugging, remove when testing if service is running
        End While
    End Sub

    Public Sub WaitForMessages()
        Dim myStr As String = ""

        Console.WriteLine("Inside WaitForMessages.")

        'While Not Me.stopping 
        While True
            While (m_itemList.Count > 0)
                ' get item from the list and process it
                m_itemList.PopFromList(myStr)
                ProcessMessages(myStr)
            End While

            Thread.Sleep(100)
        End While
    End Sub

    Public Sub ProcessMessages(ByVal msg As String)
        Console.WriteLine("ProcessMessages: {0}", msg)
        ' do processing stuff here
    End Sub


    ''' <summary>
    ''' The function is executed when a Stop command is sent to the service 
    ''' by SCM. It specifies actions to take when a service stops running. In 
    ''' this code sample, OnStop logs a service-stop message to the 
    ''' Application log, and waits for the finish of the main service 
    ''' function.
    ''' </summary>
    Protected Overrides Sub OnStop()
        ' Log a service stop message to the Application log.
        Me.EventLog1.WriteEntry("AspectMSMQWindowsService in OnStop.")

        ' Indicate that the service is stopping and wait for the finish of 
        ' the main service function (ServiceWorkerThread).
        Me.stopping = True
        Me.stoppedEvent.WaitOne()
    End Sub


    Private stopping As Boolean
    Private stoppedEvent As ManualResetEvent

    Private Sub EventLog1_EntryWritten(sender As System.Object, e As System.Diagnostics.EntryWrittenEventArgs) Handles EventLog1.EntryWritten

    End Sub

    Private Sub GetMessage()



    End Sub


End Class

Public Class ListHolder
    Private m_list As New List(Of String)
    Private m_lockObject As New Object

    Public ReadOnly Property Count() As Int32
        Get
            Count = m_list.Count
        End Get
    End Property

    Public Sub AddToList(ByVal item As String)
        SyncLock m_lockObject
            m_list.Add(item)
        End SyncLock
    End Sub

    Public Sub PopFromList(ByRef item As String)
        SyncLock m_lockObject
            item = m_list.First()
            m_list.RemoveAt(0)
        End SyncLock
    End Sub

End Class

Open in new window

0
 
LVL 9

Accepted Solution

by:
Orcbighter earned 2000 total points
ID: 38378189
OK, bit confused.
Your last post is basically the code I sent, all looks good.
Your second last post states you want to put the Queue Read in WaitForMessages - not so good, since you are also calling the Read in the ServiceWorkerThread.

So:
1. The WaitForMessages subroutine should just remove messages from the list and process them, no reading from the queue - its already done.

2. The queue.Receive(...) call is a blocking call. That means it will not return from the call until a message is received. Your subroutine will hang. The timeout parameter forces the function to return even if no message is received, after the timeout period has expired. This returns control to the ServiceWorkerThread subroutine and allows the process to test if the service has been stopped or not. So, leave the timeout parameter in.
IN addition, Your code snippet had a timeout of 5 seconds. I feel this is far too long. A better timeout is a much quicker one, say between 100 milliseconds and 500 milliseconds.
0
 

Author Closing Comment

by:petel2k
ID: 38379553
Thanks for the exceptionally clear explanations. You should create videos and charge for your solutions.

If there is any other way I'm able to help you with points here, please let me know.

You were excellent!

Thank you
0
 
LVL 9

Expert Comment

by:Orcbighter
ID: 38380172
In terms of creating a video and making money, well, from your mouth to God's ears!
Unfortunately, I have a very good face for radio :-)
cheers
0
 

Author Comment

by:petel2k
ID: 38388068
Question..

The point where I process the data..  Public Sub ProcessMessages(ByVal m As String)

I perform a service call in that sub, some calls take longer than the other's since the data comes from a data warehouse that connects to different data based on region and account passed.  

I've run several test loading 10 messages in the msg queue at a time and started the service. The first two items were quick, the next two took longer, followed by the last 6 were fast. The problem was that the last six didn't start until the two long ones were completed. Really need all to process without waiting on the other Maybe something I  missed.

Public Sub ProcessMessages(ByVal m As String)
        Console.WriteLine("ProcessMessages: {0}", m)
        ' do processing stuff here
        Dim iReqtype As Integer
       
        Dim svcinstance As New ServiceReference1.Service1SoapClient()
        
        Dim RETN As String
        Try
            'm = queue.Receive(New TimeSpan(0, 0, 5))
            'm.Formatter = New XmlMessageFormatter(New String() {"System.String"})
            'txMsg.Text = m.Body
            'txEnum.Text = m.Label
          



 ' removed code  that does proprietary stuf here 


                            '  N O W   Call the service which may take between 3 to 10 seconds depending on where the data is returned from. 
                            RETN = svcinstance.DoThis service(item, account, Now())
                            '  E N Dof service call

             
        Catch eReceive As MessageQueueException
            Console.WriteLine("{0} ({1})", _
                    eReceive.Message, _
                    eReceive.MessageQueueErrorCode.ToString)
        End Try


        'txMsg=queue.


    End Sub

Open in new window

0
 
LVL 9

Expert Comment

by:Orcbighter
ID: 38389259
To recap:
Your program has (at least) three threads. 1. The thread running the service 2. The thread running your code in ServiceWorkerThread, 3. The thread you created to run the WaitForMessages function.

Now, from your code snippet and past discussions I assume that:
* ServiceWorkerThread is looping reading from the queue and placing messages into a list.

* WaitForMessages is looping around popping messages from the list and passing them as a parameter to the ProcessMessages subroutine for further processing - your actual business logic
 
* ProcessMessages is  calling a given service and then waiting on a response from another message queue.

You can add more threads, but there is a limit. You can't give each call its own thread. In a worst-case scenario, where you can get maybe 100 calls in quick succession, and each call takes an amount of time such that none have completed before the next call comes in. If you gave each call its own thread your computer would probably hang and your service would crash.

Having said that, you can still have more threads. The thread pool has a default number of 25 worker threads per CPU. The 26th call would have to wait for the next available thread top become available.

The one fundamental problem you have is this: If your customer requires a service call that is going to take 10 to 15 seconds and block until the call completes. You just have to live with that. After all, it takes a woman nine months to give birth to a baby. You cannot get nine women to give birth to a single baby in one month!
That customer will have to wait and maybe listen to silence (or recorded music, or an advertisement, etc) until their information is returned. However, you can try to minimise the impact on other callers - the  heart of your question on Experts Exchange.

Possible fixes
----------------
There are a number of ways you could try to ameliorate this problem.
1. Re-investigate whether you can call these web services in an asynchronous manner, just in case you missed something.
2. The more-threads option:
and 3. Other options

The More-Threads Option
-----------------------
The simplest method to try first would be to increase the number of threads processing the list.

This would involve a modification to the ServiceWorkerThread function. Before the While statement, instead of creating a single thread for the WaitForMessages thread, you could create two of three such threads and see whether that alleviates the slowdown or not.

Think of a single queue of people in front of a single Bank Cash Machine. The line moves a bit like your incoming calls, each person taking his/her own time. Now, add a second Bank Cash Machine, the line moves quicker, and so on.
Think of the WaitForMessages subroutine as such a Cash Machine.

This requires a couple of changes to your code; taken from my little proof-of-concept program:
In ServiceWorkerThread, add two more threads (I also modified them to pass a thread parameter):
If (bAll_OK) Then
    Try
        ' now create the thread that will process the list of queue objects
        ThreadPool.QueueUserWorkItem(New WaitCallback(AddressOf WaitForMessages), "Threadname One")
        ThreadPool.QueueUserWorkItem(New WaitCallback(AddressOf WaitForMessages), "Threadname Two")
        ThreadPool.QueueUserWorkItem(New WaitCallback(AddressOf WaitForMessages), "Threadname Three")

    Catch ex As Exception
        bAll_OK = False
        Console.WriteLine("ERROR: Unable to create thread to process list - {0}", ex.Message())
    End Try
End If

Open in new window

The second change is in the WaitForMessages subroutine, and in the ListHolder class.
I found a bug in my code example. With more than one WaitForMessages functions running, it is possible to test if the list has a message, and then return with nothing because another WaitForMessages got there first. Very simply fixed.

(a) Modify the PopFromList method of the ListHolder class, changing it to a function returning a boolean.
(b) Optional: Added a thread parameter to the function, so I could pass information into the thread.
(b) Test whether the list has messages inside the SyncLock rather than in the WaitForMessages function, thus:
The PopFromList function of the ListHolder class:
Public Function PopFromList(ByRef item As String) As Boolean
    Dim result As Boolean = False

    SyncLock m_lockObject
        If (m_list.Count > 0) Then
            item = m_list.First()
            m_list.RemoveAt(0)
            result = True
        End If
    End SyncLock

    Return result
End Function

Open in new window

In The WaitForMessages function, modify the While loop so that it calls the PopFromList function rather than testing if the list is not empty, thus:
Public Sub WaitForMessages(ByVal stateInfo As Object)
    Dim paramString As String = CType(stateInfo, String)
    Dim myStr As String = ""

    Console.WriteLine("Inside WaitForMessages.")

    'While Not Me.stopping 
    While True
       ' get item from the list and process it
       While (m_itemList.PopFromList(myStr))
             myStr = paramString & " - " & myStr
            ProcessMessages(myStr)
        End While

        Thread.Sleep(100)
    End While
End Sub

Open in new window

0

Featured Post

Microsoft Certification Exam 74-409

Veeam® is happy to provide the Microsoft community with a study guide prepared by MVP and MCT, Orin Thomas. This guide will take you through each of the exam objectives, helping you to prepare for and pass the examination.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

This article shows how to make a Windows 7 gadget that extends its U/I with a flyout panel -- a window that pops out next to the gadget.  The example gadget shows several additional techniques:  How to automatically resize a gadget or flyout panel t…
Whether you've completed a degree in computer sciences or you're a self-taught programmer, writing your first lines of code in the real world is always a challenge. Here are some of the most common pitfalls for new programmers.
This is Part 3 in a 3-part series on Experts Exchange to discuss error handling in VBA code written for Excel. Part 1 of this series discussed basic error handling code using VBA. http://www.experts-exchange.com/videos/1478/Excel-Error-Handlin…
Exchange organizations may use the Journaling Agent of the Transport Service to archive messages going through Exchange. However, if the Transport Service is integrated with some email content management application (such as an anti-spam), the admin…

864 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question