Best Practices
As a general statement, communications and CPUs are very fast. In particular, unless the tasks are large, the computer can keep up with many users who are making small requests. However, the load on a client machine is only that imposed by a single client. On the other hand, the load on the network and the server is the accumulation of all the clients at the same time. So practices that increase message traffic, message size, or server load — even by a small amount — can have significant effects on overall throughput and capacity. Several of the following practices are aimed at minimizing these cumulative loads and thereby improving the throughput, response time, or total capacity of the system.
Clear Unused Properties
Each time the client sends an execute message to the server, the message includes fields for the NameSpace, Code, VALUE, P0 through P9, and PLIST properties. The return message drops the NameSpace and Code fields and returns the new values of all the other properties, whether or not they have changed. So the size of the message (and the time to assemble, transmit, and disassemble the message) depends on the total size of the current values of all the properties. If there are many busy clients, the effect on the network bandwidth and server capacity can become significant.
Although communications are fast and the messages relatively small, if you are sending many messages and speed is a concern, it is good practice to clear any properties that are not being used in a given message so they do not consume bandwidth. Remember that these VisM properties are intended for communication with the server and are inefficient as longer term storage on the client.
Also remember that the mirrored properties cannot be used as storage on the server, since they are created and destroyed for each message.
Disconnect Explicitly at Application Shutdown
While it is a small effect, if you shut down the client without first disconnecting from the server, you cause a TCP error on the channel. This causes an I/O error to be raised in the server code, which responds by shutting down the server job. (This is considered a valid response because the server has no further use if it cannot communicate with the client.) It is good practice to disconnect explicitly instead, allowing the server to shut down gracefully.
Recursive or Asynchronous Server Calls
All communication on each server channel is synchronous, that is, each message must be sent and received before the next message can be sent. (This requirement is enforced by Caché Direct, which will return an error condition if you try to send a message while another is pending.) For the most common case of a single-threaded client and a single server, this does not become a problem. However, there are ways around this safety – events that do not happen synchronously. Two of these are timer events and the Visual Basic DoEvents() method.
Timers on the Client
A timer is, by definition, an asynchronous event. They can be set up so that they do not interfere with whatever other processing is happening. For example, a timer could be set up to check on progress or if some event has happened on the server. But if you send a message to the server that results in a long operation, you tie up that channel for the duration of the operation. If you wish to check on progress, you must either have the server call back periodically and report progress or have the client send an inquiry on a different channel. Another way to perform a long server operation that does not tie up the client is to Job off a separate process to do the work and then return to the client. This does not require another channel, but it does still require a separate job. If a timer goes off while a message is being processed, it can result in an attempt to send another message. In this case, Caché Direct will reject the message with a “nonsynchronous communication” error.
Visual Basic DoEvents Function
Visual Basic has a built-in function, DoEvents, that is an explicit call to the Windows event loop. It is often used to allow an immediate repaint while other operations are still in progress. The hazard appears if DoEvents causes a server message while one is already in progress. (For example, DoEvents would allow the user to continue with another task. This task could result in a server message.) In theory, this can only happen in a callback. DoEvents is particularly onerous in this case, since a new, recursive message will be sent to the server, probably destroying the context of the original message. From there, it is a slippery slope. As a general rule, do not have a callback that calls DoEvents. If you do, the application should have some sort of flag that disables user input or at least disallows calls to the server until the stack has been unwound.