SuperServer v's Classic Architecture
This article was written about InterBase by Borland in the mid-1990's. The factual description still applies to Classic and SuperServer in Firebird in 2006, but the implications related to project direction and reliability do not apply to Firebird. The Firebird project fully supports the Classic architecture on all platforms and recommends its use in multi-processor environments.
The InterBase SuperServer Architecture
SuperServer is a multi-client, multi-threaded implementation of the InterBase server process. This implementation replaces the "Classic" implementation used for previous versions of InterBase.
SuperServer serves many clients at the same time using threads instead of separate server processes for each client. Multiple threads share access to a single server process. The benefits of SuperServer architecture include:
- Having a single server process eliminates bottlenecks resulting from arbitration for shared database pages and reduces the overhead required for multiple process startups and database queries.
- SuperServer improves message interaction performance because a shared library call is always faster than an interprocess communication request to a server process.
- SuperServer improves database integrity because only one server process has write access to the database, rather than one process for each client. All database engine functionality is encapsulated into a unified, protected subsystem that is isolated from user application error.
- SuperServer allows for the collection of database statistics and user information that InterBase's tools can use for performance monitoring and administrative tasks.
- SuperServer is more cost-effective than the Classic architecture. All operating systems have limits on the number of OS processes that can run concurrently. SuperServer allows for a fixed number of database threads to be multiplexed over a potentially large number of concurrent database connections. Since these threads are not hard-wired to any specific database connection, SuperServer can support a larger number of users with minimum resources use.
The InterBase Classic Architecture
Classic architecture, the design in InterBase 4.0 and earlier, was process-based. For every client connection, a separate server process was started to execute the database engine, and each server process had a dedicated database cache. The server processes contended for access to the database, so a Lock Manager subsystem was required to arbitrate and synchronize concurrent page access among the processes.
Invoking the Classic Server
The InterBase Classic server runs on demand as multiple processes. When a client attempts to connect to an InterBase database, one instance of the gds_inet_server executable runs and remains dedicated to that client connection for the duration of the connection.
The initiator of gds_inet_server is inetd, the UNIX service turnkey process. It has a configuration file, /etc/inetd.conf, that associates services with the executable that is to receive the connection. When inetd receives a connection request for a given service, it looks up the appropriate program in /etc/inetd.conf, executes it, and transfers the network connection to the service program.
When the client chooses to disconnect, gds_inet_server closes its connection to the database and any other files, and then exits. When there are no clients connected to any database, there should be no invocations of gds_inet_server running.
Lock management is taken care of by another process, gds_lock_mgr. This program is started when the second client attaches to a given database. The job of the lock manager is to serve (metaphorically) as a traffic cop. It grants locks on database resources to clients. It also requests that clients relinquish locks on a resource when that resource is in demand by other clients. The gds_lock_mgr remains running even after the last client disconnects. The next time a client connects, it can avoid the slight overhead of starting the lock manager process.
Use of Posix Signals
The gds_lock_mgr process communicates with each client process by using a shared memory area, and a signalling mechanism using the POSIX signals SIGUSR1 and SIGUSR2. Signals are caught in signal handling routines in libgdslib.a, and for this reason user applications should not perform signal handling or any modification to the signal mask. Applications which need to use POSIX signals must compile with an alternate InterBase library, libgds.a. This library functions identically to libgdslib.a, but it handles signals sent by the lock manager in a child process called gds_pipe. All client applications compiled with libgds.a automatically run with this child process. No changes to application code are needed, only a different linking option.
Each instance of gds_inet_server keeps a cache of database pages in its memory space, which is likely to result in some duplication of cached data across the system. While the resource use per client is greater than in SuperServer, Classic uses less overall resources when the number of concurrent connections is low.
Local Access Method
The Classic architecture permits application processes to perform I/O on database files directly, whereas the SuperServer architecture requires applications to request the ibserver I/O operations by proxy, using a network method. The local access method is faster than the network access method, but is only usable by applications which run on the same host as the database.
Monitoring Database Connections
The database information call for active connections always reports exactly one connection on a Classic server, no matter how many clients are connected to databases on that server. The reason for this is that every client connection has its own gds_inet_server process on the server, and each instance of that program knows only about its own connection. Only in SuperServer does the server process have the ability to report all client connections on the server.
In order for InterBase Classic to work with a mixture of local and remote clients running as different user ID's, the server executables gds_inet_server and gds_lock_mgr must run as root. The processes must run with a real uid of root to set their effective uid to that of the client uid. The lock manager must have the superuser privilege to send signals to the processes.
In some IT environments, the presence of executables with setuid bits turned on raises concerns about security. Nevertheless, do not change the runtime configuration of InterBase server. The setuid root configuration of the Classic software is important to its function.
Because applications can run as any uid, database files must be writable by all uids that access the databases. To simplify maintenance, database files are created writable by the whole world. With care, you can restrict these file permissions, so that the database files are safe from accidental or deliberate damage. Make sure you understand file permissions completely before attempting this, because all local and remote clients need write access to the database, even if they intend only to read data.
Comparing Classic and SuperServer
SuperServer runs as a single process, an invocation of the ibserver executable. ibserver is started once by the system administrator or by a system boot script. This process runs always, waiting for connection requests. Even when no client is connected to a database on the server, ibserver continues to run quietly.
The SuperServer process is not dependant on inetd; it waits for connection requests to the gds_db service itself.
The SuperServer process is a multi-threaded application. Different threads within the process are dedicated to different tasks. For instance, one thread waits on the gds_db service port for incoming connection requests. Other threads are analogous to individual gds_inet_server processes in the Classic model, serving client queries. Another thread serves as the lock manager, replacing the gds_lock_mgr process from the Classic model.
The lock manager in SuperServer is implemented as a thread in the ibserver executable. Therefore InterBase does not use the gds_lock_mgr process. Likewise, POSIX signals are not used by the lock manager thread in SuperServer; interthread communication mechanisms are used.
The SuperServer implementation has less overhead and uses fewer system resources per client connection than the Classic model. SuperServer has one cache space for all client attachments, allowing more efficient use of cache memory. For these and other reasons, SuperServer has demonstrated an ability to efficiently serve a higher number of concurrent clients.
Threaded Server and UDFs
User-Defined Functions (UDFs) are libraries of functions that you can add to extend the set of functions that the InterBase server supports. The functions in your UDF library execute within the process context of the InterBase server. Due to the threaded implementation of SuperServer, there are issues with UDFs that require that you write UDF functions more carefully than when writing UDFs for a Classic server.
You must design UDFs for SuperServer as thread-safe functions. You cannot use global variables in your UDF library, because if two clients run the UDF simultaneously, they conflict in their use of the global variables.
Do not use thread-local global variables to simulate global variables. SuperServer implements a sort of thread pooling mechanism, to share threads among all the client connections. It is likely that if a given client executes a UDF twice, that each execution is not executed in the context of the same thread. Therefore, you cannot depend on thread-local variables keeping values from one execution of the UDF to the next for a given client.
UDFs that allocate memory dynamically run the risk of creating a memory leak. Because SuperServer is supposed to stay up and running indefinitely, not just for the duration of the client connection, memory leaks can be more damaging in SuperServer than in Classic. If your UDFs return dynamically allocated objects, then you must use malloc() to allocate the memory for these objects (on Win32, you must use ib_util_malloc() or the malloc() that is part of the Microsoft Visual C++ runtime library). Do not use new or globalalloc() or the Borland malloc(). Finally, such functions must be declared in databases with the FREE_IT option of the DECLARE EXTERNAL FUNCTION statement.
By contrast, in Classic, there is a separate process for each client connection, so the UDFs are guaranteed not to conflict. Global variables are safe to use. Also, memory leaks are not as dangerous, because any leaked memory is released when the client disconnects.
InterBase recommends that you design UDFs for SuperServer, the more restrictive model, even if you use a version of InterBase implemented with the Classic model. Eventually InterBase will be implemented with SuperServer on the platform you use. If you design UDFs with this assumption, you can upgrade to a later version of InterBase without the risk that your UDFs must be redesigned to work with SuperServer.
SuperServer can be configured to run as a non-root uid, for enhanced security. In SuperServer, you can restrict the permissions on database files to allow only the InterBase server uid to access the database.
Why Two Implementations?
The Classic implementation predates the SuperServer implementation, and the SuperServer implementation is the future of InterBase.
Classic configuration is used on operating systems that currently don't have the technology for threaded applications, which is required for SuperServer. InterBase also distributes the Classic version on platforms that have threading technology, but which benefit from the low-profile implementation.
SuperServer has a greater ability to meet the demands of a growing multi-user system, while retaining good performance and efficiency. SuperServer is implemented in InterBase product on all platforms where it is technically practical. It is the intention that SuperServer is the future direction of InterBase on all platforms.