Tuesday, May 28, 2013

External Components – Database Query Implementation Update

External Components – Database Query Implementation Update

Background:

As mentioned in the last post, my Framework and database interface component were unable to handle the publish and delivery of a query response when the database interface component sent a database query to the external database component/application process.  The publish of the query response was successful but its delivery back to the requesting component failed.

At the time I thought it was because the request topic message had been released when the database interface component returned control to the Framework after sending the query to the external database component/application.  Therefore, I added code to the Framework to detect that the requesting component had subscribed for a response but that the response had yet to be published.  This to avoid releasing the request too soon.

This required a number of changes to keep track of components that subscribed for a response versus those that hadn’t and set when the response was published so that the release code could avoid the release when the response had yet to be published.

Before I had finished with these changes I recognized that they were not going to provide the solution.

That is, first of all, the fact that the request had been released was not the reason that the Framework lacked the ability to determine which component was the requestor.  This had been based upon the consumer of the request producing and publishing the response before returning to the Framework.  Therefore, when published, the Framework could determine the publisher of the last request topic message that it had delivered to the consumer.  And hence could forward the response to it when it was published.

Prior to the publish of the delayed response, the consumer component had been activated once again by the publish of the wakeup topic by the Named Pipe Receive component as it queued the received response message from the external database.  Since the receive thread is really a subcomponent of the database interface component (that is, DBI1 – see diagram of the April 29 post "External Components – Database Queries") the producer of this request message is the same as the consumer.  Therefore, the Framework in checking the producer of the last request that activated the consumer component, no longer was able to determine the producer of the original query request.

To solve this problem while allowing delayed responses (that is, responses that are published after the consumer component has returned control to the framework following receipt of the request) would require further changes.

And, to complicate matters further, multiple request producers are allowed.  Therefore, a number of requestors could concurrently publish a request.  With immediate response the framework, via the framework component Scheduler, queues these requests to the run queue for the thread priority of the consumer component.  When the consumer returns to the framework, the Scheduler activates the message topic callback of the next component in the particular run queue.  Therefore, if multiple requestors concurrently published request topics, the other requestors would have their requests in the queue and the consumer component would be immediately reactivated to treat the next request in the queue and the framework would save this requestor as the one to which to return a response.

If delayed responses were to become allowed, the framework would deliver the next request before the response to the previous request had been received by the DBI1 component (acting as the middle man between the requesting component and the external database component).  This new query could then be sent to the external database component or it would be necessary to queue it in the DBI1 component.  If allowed to be sent to the external database component it might interfere with the previous query or be for a different database.  If for a different database, that database might provide faster results than that of the previous query causing its response to be returned to the receive queue before that of the first query.  Then the responses would be published out of order of the requests requiring further solutions to be developed.

Better to change the framework to avoid selecting the next request topic of the run queue until the delayed response is published.  This would continue to keep track of all the requestors that had concurrently published by the existing mechanism – as changed to wait until the response was published.

Before I thought of the above possibility as a fix, a different solution occurred to me and I decided to implement it.  I completed and retained the delayed response changes that I had already made in case I might continue with them later.  That is, those changes that would avoid the release of the request until the response was published which would prevent the requestor from writing and publishing a new request until its previous request had completed.

It’s well that I did since further reflection reveals that such a change wouldn’t have worked anyway.  That is, a change to avoid examining the run queue until the response was sent would also prevent the receive of the Read the Receive Queue wakeup event.

The Alternative Solution:

In the alternative solution the Database Interface component has another subcomponent in addition to the instantiation of the Named Pipe Receive thread.  This additional subcomponent is the thread that communicates with the external database component while the main component thread waits for the query response so that the query response can be interpreted and published as the response to the request topic.  The main component is the one that subscribes to consume the requests and publishes the responses to be returned to the requesting component by the framework. That is, it is the interface to the requesting components. 

Since the main component waits for the completion of the query, the publish of the delayed response acts the same as an immediately published response to the framework.  That is, it doesn’t exit back to the framework until after it has published the response. 

Therefore, any concurrent requests by other requesting components remain in the Scheduler run queue of the thread priority level.  And the component that made the request (along with those that made the queued concurrent requests) cannot use the request buffer to write and publish a new request.

It’s just that the main component becomes a long duration component the same as if it had complicated time-consuming computations to perform.

I implemented this as in the following diagram.

    +––––-––––-––––-––––--+ 
    |                     |
    |      Framework      |
    |                     |
    +––––-––––-––––-––––--+ 
               Λ         \
               |          \
  +-–––––––––––|–––––––––––\––––--–––––––––––––––––––––––-–––––––-+
  |            |            \    DBI1 Package/Component           |
  |            |             \                                    |
  |            V              \                                   |
  |   +----–––––––----–––-+    V+----–––––––----––-+              |
  |   |      Message      |     |     Database     |              |
  |   |      Portal       |     |     Manager      |  +––––––––-  |
  |   |       sub         |     |       sub        |  | Receive   |
  |   |     component     |     |     component    |  |  Queue    |
  |   |             /---\ |     |                  |  +––––––––-  |
  |   |             | P |<-–\   |                  |      Λ       |
  |   |             \––-/ |  ––-|                  |      |       |
  |   +----–––––––----–––-+  C  +----–––––––----––-+     /        |
  |                                          |          /         |
  |                                          |         /          |
  +-––––––––––––––––––––––––––––--–––––––––––|––––––––/––––––––-–-+
                                             |       /
         P = Pause                           V      /
         C = Continue event             +––––-––––-–––––-+
                                        |    External    |
                                        |    Database    |
                                        |    component   |
                                        |                |
                                        +––––-––––-–––––-+

In the diagram the main component is called the Message Portal subcomponent while the subcomponent that communicates with the External Database component is the Database Manager subcomponent.  (Architecture-wise, the main component is just the initially registered component that is assigned the initial component key.)

The main component was named the Message Portal since it is the subcomponent to subscribe to consume the message request topics that the Database Interface Ada package (C++/C# class) treats and then to publish the response to be delivered back to the requesting component.

The Database Manager subcomponent is the one that manages the databases and communicates with the External Database component.  It is the subcomponent that tracks when both the connection to the named pipe has occurred and when power up processing has completed such that queries can be sent to access the database.  Eventually it will also monitor the External Database component, receive notifications when a connection between the External Database component and a particular database has been completed or lost, and send commands to establish such a connection or close it.

As such, as illustrated in the diagram, it also has an interface to the framework to receive instances of topics such as power up complete or a periodic wakeup to perform monitoring.  Likewise, to receive the wakeup that indicates that a message from the External Database component has been received and queued.  But, to avoid the delayed response problem, it must not subscribe to treat query requests or any other request that needs a response unless it can publish an immediate response (– meaning before the callback returns to the framework).

The Message Portal is activated by the framework when it is able to deliver a query request of another component.  The Message Portal will then interpret the request topic and form a query message formatted correctly for the particular database that will obtain the data from which a response can be created.  It then buffers the query message, sends a wakeup message to the Database Manager subcomponent and enters its Pause to await the response. 

The Database Manager is run by the framework via the callback for the particular wakeup topic and then retrieves the query message and transmits it to the External Database component via the named pipe that will cause the particular database to be accessed that will obtain the response.  When the External Database transmits the response the External Pipe receives it, queues it, and sends a read the queue wakeup request to the framework.  The framework than causes the Database Manager to run via its callback for this topic and the Database Manager buffers the response and signals the Message Portal to Continue.

The Message Portal then retrieves the buffered query response and extracts the necessary data to publish the response to the original request.  Upon doing the Publish the callback procedure for the original request returns to the framework completing the processing of the request.

Instead of the Message Portal buffering the query message for retrieval by the Database Manager, the interface between them could be to publish a query request topic message for which the Database Manager would subscribe to consume (as long as no response is involved).  However, the response still must be buffered for retrieval by the Message Portal since it is waiting for the response to be ready as it cannot receive a wakeup message.  The ability to react to a wakeup message would revert to the original problem.  Therefore, for symmetry, I buffered both the query and the query response for retrieval by the opposite subcomponent.

The two messages can be buffered since only one request will be treated at a time.  Therefore, the access of the buffer is thread safe since the Message Portal will buffer the query message and then wake the Database Manager to treat it.  Likewise, the Database Manager will buffer the query response and then signal the Message Portal subcomponent to treat it.  Neither will use the buffers again until the next request is received by the Message Portal.

Unexpected Problem:

In my initial implementation to try this idea, the Message Portal did the Pause as a delay loop using the Ada delay statement while checking a Continue boolean as the signal to exit the delay loop.  Although I didn’t intend to retain this mechanism it worked as anticipated and was thread safe since a unary object was being monitored and set. 

However an unexpected problem occurred when the Message Portal received the request topic and sent the wakeup to the Database Manager.  The Database Manager didn’t run.

Luckily I soon thought of the reason.  Deadlock. 

The two subcomponents had been registered with the same expected time interval for processing.  Therefore, their threads were assigned to the same priority level and to the same Scheduler run queue.  Because of this the wakeup for the Database Manager subcomponent was added to the run queue behind the request that was being treated by the Message Portal subcomponent.  As such it would stay there until the Message Portal returned control to the framework.  Which it wasn’t going to do until it was informed that the query response was ready for interpretation.

The problem occurred since the Message Portal should have been assigned a longer expected time interval for processing.  Although it can handle its interpretation of the request quickly enough it is going to wait until the External Database component receives the query message, presents the query to the database, and the response is transmitted back to the Database Manager subcomponent.  Therefore, the Message Portal subcomponent needed to be assigned to a longer duration thread and the topic message to its associated run queue.

With this fix everything worked fine.

Subcomponent Pause Resources:

The next modification was to implement a Pause/Continue resource similar to a semaphore for use by subcomponents. 

Components can only communicate with each other via the framework topics where one component can signal any other component that’s interested (that is, has subscribed to consume the particular topic) that there is new data to be read.  Or the consuming component can just read the latest values whenever it runs.

To retain this component independence a Pause/Continue resource was added where a particular resource can only exist for use by the subcomponents of a component. 

To implement this resource the registration of components was adjusted such that a subcomponent can supply the key of the previously registered component/subcomponent so that all the fields of the private key began to be used.  This allowed the recognition of a subcomponent group by the framework while assigning a unique key to each subcomponent.

Then a new register interface was added to request a Pause/Continue resource be assigned by supplying the component key.  The Pause and Continue interfaces are supplied the resource key which, by programming convention, is to be privately retained within the Ada package body similar to the private code of a C++ or C# class.  The framework checks that the key supplied to create the resource refers to a component and that the resource key used by a Pause or Continue request is for a subcomponent of the component that owns the resource.  Therefore, subcomponents can signal each other within a component but not other components.

Otherwise, the Pause/Continue resource is just another event as far as the framework is concerned and so was simple to implement.

The implementation allows multiple resources to be assigned to a component since more than one subcomponent might need to Pause.  Also, a resource kind was specified so that other kinds of resources can be added in the future if warranted.  

The Pause/Continue resource allowed me to replace the Ada delay loop with the invocation of a Pause and the Database Manager to signal that the query response was available by invoking the Continue.

Finishing Up:

Because of these problems I also recognized that long duration upon demand components could block each other if assigned to the same thread priority run queue.  To prevent this from happening (so that they would have to share computer execution by round robin or other means of the operating system) I modified the allocation of run queues such that only short duration components could be assigned to a shared run queue.  Therefore, upon demand (that is, event driven) long duration components will run, except for how initiated, the same as aperiodic and pseudo periodic components.




No comments: