COM+ 1.5 provides two new application lifecycle management options: application pooling and recycling. Both options are configurable on a new tab on the application’s properties page. Pooling and recycling services are available only for a server application. You cannot configure library applications to use pooling and recycling because they do not own their hosting process. Library applications have, in effect, the pooling and recycling parameters of whatever server application happens to load them.
Application
pooling
allows you to configure how many surrogate
processes are launched to host your server application’s
components. Under COM+ 1.0, all instances of components from a server
application always share the same hosting process. Although this
sharing is also the classic COM default, classic COM local server
developers had the option of allocating a process per object (by
registering the class factories with the
REGCLS_SINGLEUSE
flag). COM+ 1.5 gives you explicit control
over how many processes are launched by configuring a processes pool.
The application properties page now contains the
Pooling & Recycling tab (see Figure B-9). You can configure
the pool size in the Pool size edit box. The default pool size is
one—a single process hosts all instances of components from the
application, like in COM+ 1.0. However, if you set it to a value
greater than one, COM+ 1.5 creates a process per each new instance
until it reaches the pool size, at which point COM+ starts
multiplexing new instances to the existing processes, apparently in a
round-robin fashion. The maximum configurable pool size is 999,999,
enough for all practical purposes. Application pooling is useful as a
fault isolation technique. If one process has to shut down because of
some error or exception, the other processes and their clients are
not affected. Application pooling also gives you same-machine load
balancing—you do not have to use a separate load balancing
service with multiple machines to allocate different instances to
different processes. The pool size is available as the
ConcurrentApps
named property of an application
catalog object.
If you start a COM+ 1.5 application manually or programmatically, COM+ 1.5 creates as many processes as the configured pool size. This behavior is analogous to component minimum pool size, discussed in Chapter 3, and it only comes into play when the application is started explicitly. This behavior is useful when you want to mitigate anticipated spikes in client requests—you shouldn’t pay the overhead of creating new processes (and potentially, pools of objects in those processes as well).
The other new application lifetime management service is recycling. Application recycling is used to increase overall application robustness and availability by compensating for code defects. For example, one of the most common defects is a memory leak. Not all products have the necessary quality assurance resources or commitment during development; as a result, memory leaks can be present in the released product. An issue arises when a COM+ application can be left running indefinitely servicing clients. Even a very small memory leak can have a devastating effect over a long period of time. For example, imagine a system with an “insignificant” memory leak of only 10 bytes per method call. A modern system that processes in excess of 100 transactions per second will, after one day, leak 100 MB of memory. The process hosting the application will consume this amount of memory, thus severely hampering performance and availability, as a result of additional page faults and memory allocation penalties. The way developers treated such a leak in COM+ 1.0 (other than fixing it) was by periodically terminating the hosting process and restarting it. This technique is called application recycling. COM+ 1.5 allows you to configure automatic recycling on the Pooling and Recycling tab (see Figure B-9). You can have COM+ recycle the process when it reaches a memory limit (the Memory Limit edit box) to cope with memory leaks. A value of zero is the default value, which means no limit.
By specifying the Lifetime Limit value, you can also instruct COM+ to shut down your application after a predetermined amount of time. This instruction allows you to cope with defects in handling other kinds of resources (such as system handles) by specifying the Lifetime Limit value. A value of zero is the default value, which means no lifetime limit. Note that the semantics of the lifetime limit is different from the idle time management option on the application Advanced tab. The Server Process Shutdown value on the Advanced tab specifies after how many minutes of idle time (i.e., not servicing clients) to shut down the application. The lifetime value specifies after how many minutes to shut down the application, irrespective of the work in progress inside the process.
COM+ provides two more recycling triggers. You can have COM+ recycle your application after a specified number of method calls into your application by specifying such a limit in the Call Limit edit box. The number of calls is defined as combined number of calls made on all objects in the application. The default value is set to zero—no limit. You can also request application recycling after a certain number of activations. Activations is defined as the total number of objects that COM+ 1.5 created in that application. You specify the activation limit in the Activation Limit edit box and, again, the default value is set to zero.
Regardless of how the decision to recycle the application is made (the memory limit reached, the lifetime elapsed, or the call or activation limit was reached), COM+ 1.5 routes new activation requests to a new host process and waits for existing clients to release their references to objects in the recycled process. However, you can specify how long COM+ 1.5 should wait in the Expiration Timeout edit box. After that expiration timeout, COM+ 1.5 terminates the application, even if clients are still holding live references. The default expiration timeout is 15 minutes.
Finally, note that recycling is not available for a COM+ application configured as system service, nor can you recycle a paused application.
The COM+
1.5 Catalog provides you with programmatic ability to configure the
recycling parameters discussed previously. To configure memory and
time-bound recycling, use the
RecycleMemoryLimit
and
RecycleLifetimeLimit
named properties of the
application’s catalog object. To configure the expiration
timeout, use the
RecycleExpirationTimeout
named property. To configure call or
activation limit programmatically, set the values of the
RecycleCallLimit
or
RecycleActivationLimit
named properties.
Example B-1 shows how to set a recycling limit
programmatically. It implements the SetRecycleByActivations( )
helper function, which sets a specified limit of activations for
recycling a specified application.
Example B-1. Setting a recycling limit programmatically
//usage: "MyApp" will be recycled after 1000 object activations //hres = SetRecycleByActivations("MyApp",1000); HRESULT SetRecycleByActivations(LPCSTR lpcszAppName,DWORD dwActivations) { //Verify app name is valid if(_bstr_t(lpcszAppName) == _bstr_t("")) { return E_INVALIDARG; } HRESULT hres = S_OK; ICOMAdminCatalog2* pCatalog = NULL; hres = ::CoCreateInstance(CLSID_COMAdminCatalog, NULL,CLSCTX_SERVER, IID_ICOMAdminCatalog2,(void**)&pCatalog); ICatalogObject* pApplication = NULL; ICatalogCollection* pApplicationCollection = NULL; long nApplicationCount = 0; int i = 0;//Application index //Get the application collection hres = pCatalog->GetCollection(_bstr_t("Applications"), (IDispatch**)&pApplicationCollection); pCatalog->Release( ); hres = pApplicationCollection->Populate( ); hres = pApplicationCollection->get_Count(&nApplicationCount); hres = COMADMIN_E_OBJECT_DOES_NOT_EXIST; for(i=0;i<nApplicationCount;i++) { //Get the current application hres = pApplicationCollection->get_Item(i,(IDispatch**)&pApplication); _variant_t varName; pApplication->get_Name(&varName); _bstr_t bstrName(varName); if(bstrName == _bstr_t(lpcszAppName)) { long ret = 0; _variant_t varActivationLimit((long)dwActivations); hres = pApplication->put_Value(_bstr_t("RecycleActivationLimit"), varActivationLimit); hres = pApplicationCollection->SaveChanges(&ret); } pApplication->Release( ); } pApplicationCollection->Release( ); return hres; }