How You Can Manage State in Private and Shared Servers
The Remote Server Initiate method can request either a private or shared server (shared is the default). This method would not typically be invoked directly; it would be invoked implicitly by a client side Name Server Connect() method that reads the Initiate parameters from the server side Name Server process. These configuration parameters are maintained using the Server Manager.
Using a private server avoids complications that can arise when interleaving concurrent requests from multiple clients in a shared server. However, the performance cost is high because a new server process must be launched each time a client calls the Remote Server Initiate method. Also, it is less scalable to large numbers of clients because each client must have a separate, dedicated server process on the server machine.
Using a shared server requires special care on the part of the application developer because remote 4GL procedure calls from different clients can be interleaved in any order. Simply maintaining state in global variables from one call to the next is not appropriate in this environment.
Remote calls are serialized, so that the 4GL need not manage concurrent threads within the scope of one remote call. However, the application must be designed to allow switching client context between one call and the next. This means that you must take care to associate the appropriate application state with the appropriate client. This can be achieved manually or through ASOLib. ASOLib contains components that automate application state switching.
ORSPO.EXE: Configuration Options
The SPO can be configured to control the way it manages its ASO slave servers. This configuration information is stored in the orserver.json configuration file, and should be set up using the Server Manager.
The ORSPO.EXE (or SPO) optionally can be configured to manage multiple ASO server processes for each application signature (the combination of ImageFile and CmdFlags strings specified on the Initiate method constitutes an application's signature.) In that configuration, the SPO automatically performs load balancing between the available servers on each remote call. This means that each time a particular client makes a remote call, it may be handed to a different server instance for processing. Any client state saved in the global variables of one server process during a particular call is not visible to another server process that may handle the next call from that client.
The SPO can also be configured to shut down a server process when it is inactive for a specified length of time. By default, this timeout interval is one hour. When a new client does an Initiate with that signature, a new server process is started. Any application state that needs to be passed along from one server instance to its replacement must be stored outside of the server variables, preferably in a database. Servers may time out even when clients are still connected if the client is inactive for the timeout interval. In this case, one call from a particular client may be serviced by one server instance, and the next call from that client may be serviced by a replacement server instance.
Even though the SPO can be configured to never time out an ASO server process, 4GL runtime errors and database errors may cause a server process to be shut down and restarted. This is a safety measure to ensure that a possibly corrupted server does not continue to run. If you want your application to handle 4GL runtime errors and keep the server instance running, you can clear the error status using a special method on the session object:
CurSession.ClearErrorFlag()
Any errors that occurred prior to the ClearErrorFlag call are then invisible to the SPO.
Besides allowing the server process to continue running, clearing the error flag enables the ByRef parameters to be marshaled back. When an error is detected, the server does not attempt to marshal back any parameters. The SetExitTrap feature is helpful in handling these error conditions.
One simple technique for managing client state in a shared server environment is to have each client hold a structure containing its own state variables, and to pass that structure as an extra by-reference parameter on all remote 4GL calls. The server application can then use that structure to restore the current client's application state at the beginning of the call and save any changes in that client state before returning.
Alternatively, you can reduce network load and produce a reusable fat server application that uses ASOLib to retain persistent state in the server process for each client session.