Full-text Search is a service that can be utilized by a Lotus Notes user, an agent, a browser, another server, etc. "Not enough memory" errors indicate a memory resource is being consumed at a rate that exceeds what is available.
On a heavily loaded server, this lack of available memory exposes other functional areas to memory resource issues. Those tasks or processes that are less robust when such resource limitations are encountered, fail to recover. This eventually leads to server instability.
Queries performed through HTTP are particularly vulnerable, given the JVM also requires significant memory resources within the same process. The most common error messages seen when a search fails with memory issues at the GTR level are Full Text message: Memory allocation error. errcode = 3301 and/or Full Text message: Memory allocation error. errcode = 256: Not enough memory for full text indexing or search. The GTR (General Text Retrieval) is the search engine employed by FTSearch.
Complex queries are more likely to consume large amounts of memory and expose this type of memory allocation failure. The full text query syntax is rich with options, but in certain combinations, with enough content to search through, memory resources can be exhausted. Wild card operators are particularly vulnerable.
This type of scenario may be exacerbated with an increasing number of sessions utilizing similar queries. In a heavily loaded server environment, memory resources are exhausted. The example query below would be typical of the type of syntax usage sufficient to cause such an error:
[Field1] = "aaa*" OR [Field1] = "bbb*" OR [Field1] = "ccc*" OR [Field1] = "ddd*" OR [Field1] = "eee*" OR [Field1] = "fff*" OR [Field1] = "ggg*" OR [Field1] = "hhh*" OR [Field1] = "iii*" OR [Field1] = "jjj*" OR [Field1] = "kkk*" OR [Field1] = "lll*" OR [Field1] = "mmm*" OR [Field1] = "nnn*" OR [Field1] = "ooo*" OR [Field1] = "ppp*" OR [Field1] = "qqq*" OR [Field1] = "rrr*" OR [Field1] = "sss*" OR [Field1] = "ttt*" OR [Field1] = "uuu*" OR [Field1] = "vvv*" OR [Field1] = "www*" OR [Field1] = "xxx*" OR [Field1] = "yyy*" OR [Field1] = "zzz*"
Memory resource errors may be more prevalent due to the complexity of the search. In some instances, these searches can cause low memory conditions to the point of crashing the server. Other causes may be due to the memory configuration of the server, lack of adequate physical memory, size of the database and/or full-text index or platform limitations.
Memory Resource Configuration
To begin with how configuration plays a factor here, below is an example of how the general memory limits for 32-bit and 64-bit applications impact resources available:
|General memory limits||32-bit||64-bit|
|Physical memory (RAM)||4 GB||128 GB|
|Total virtual address space (based on a single process)||4 GB||16 GB|
|Virtual address space per 32-bit process||2 GB (3 GB if system is booted with /3GB switch)||4 GB if compiled with /LARGEADDRESSAWARE (2 GB otherwise)|
|Virtual address space per 64-bit process||Not applicable||8 GB|
|Paged pool||47 megabytes (MB)||128 GB|
|Non-paged pool||256 MB||128 GB|
|System page table entry (PTE)||660 MB to 900 MB||128 GB|
It is evident from this table that the memory available for 64-bit is far greater than 32-bit. An application that utilizes complex queries against a large data set will likely exhaust memory very quickly on a 32-bit OS. Also evident is the types of memory available. How to accurately measure and leverage available memory starts with accurately measuring process memory usage. When an administrator views the "Mem Usage" statistic in Task Manager. "Mem Usage" is not an accurate indication of the amount of memory allocated or mapped into a process' memory space. Instead, "Mem Usage" indicates the amount of physical memory currently being used by a process (equivalent to "Working Set" in PerfMon).
In order to more accurately assess process memory usage, an administrator should look at "Virtual Memory Size" in the Processes tab of Task Manager (or "VM Size"). This statistic shows the total amount of private virtual memory or address space for a process, i.e. what's allocated from the process' heap, either as physical memory or from the pagefile (equivalent to "Private Bytes" in PerfMon).
Below is a table that correlates Task Manager and PerfMon memory metrics:
|PerfMon Counter||Task Manager Metric||Description|
|Process->Working Set||Mem Usage||The amount of physical memory which is directly (currently) allocated to the process. It can be accessed without causing a page fault. This includes pages shared with other processes|
|Process->Private Bytes||Virtual Memory (VM Size)||The total private virtual memory allocated to the process (physical or page)|
|Process->Virtual Bytes||N/A||The total overall virtual memory allocated to the process (either private or shared, physical or page).|
Since "Mem Usage" is based on physical memory, this may include both process-private memory and shared memory. As a result, "Mem Usage" may be higher or lower than "VM Size" depending on server activity. This number may also climb suddenly due to the fact that a shared memory segment has been paged into physical memory, when in fact, no additional memory has been allocated.
The Process->Virtual Bytes counter in PerfMon is the only direct indication of ALL the memory a process can see, either shared or private, physical or pagefile. There is no Task Manager equivalent to this metric. This is the real indication of how much memory is mapped into a processes memory space. The Procmon utility also provides process statistics that can be utilized to measure physical vs virtual memory allocated on a process basis.
Query Syntax and Size of Working Result Sets
To understand why a particular query syntax may be more memory intensive than another, we can examine some example queries and databases created to exercise memory consumption as it applies to result sets and query complexity. When full-text indexes grow in size as expected with database size increases, the average size of document results will grow linearly. The biggest threat to good search performance is lack of scalability due to:
a. Size of index
b. Type/Complexity of query
c. Size of results or working set
d. Numbers of concurrent searches (expressible in searches/sec)
Generally speaking, in Domino full text searches, the more documents found by a search, the more memory resources are required. The next two charts were composed using a dataset with **specifications below. This first chart compares memory consumption in Mb for a single query producing a result set illustrating a single document being returned versus hits returned for all documents in the database. Number of results is a factor.
Query complexity can also be a major factor in memory consumption. Certain operators like wildcard and "or" will increase memory consumption dramatically. Size of database/index is also a significant factor. This chart below indicates how a single complex query can be optimized to reduce memory consumption ( in Mb) and improve overall performance. Optimization was achieved by examining what terms/operators were being employed and how variations to that query syntax would allow the same results ( single document) to be returned simply by re-organizing the data and/or query syntax.
Queries used to demonstrate types/complexity were as follows:
Single Doc: "abdc9"
All Docs: "a*"
Complex:"(a* or b* or c* or abcd*) and ([docnumber] > 1990 and [docnumber] < 1992)"
Partial Optimization: "abcd* and ([docnumber] > 1990 and [docnumber] < 1992)"
Full Optimization: "([docnumber] > 1990 and [docnumber] < 1992)"
While not every query can be optimized to this example, examining those queries that consume excessive amounts of memory is the first step to optimization. Complexity of query is a factor.
To illustrate how number of concurrent searches impact FTSearch performance, the charts below indicate Multi thread timings taken in comparison to various working sets. ( Not using **Specifications)
Search Speed by number threads by number of documents
** Specification: Data sets for memory resource comparison consists of three databases of uniform design and content with number of docs ranging from 9103 (large 1.3 Gb) , 5967( Medium .603 Gb) and 2000 (.072 Gb) documents. The FTI sizes generated were 235 Mb, 119 Mb and 17.6 Mb respectively.
Diagnosing the problem
The following debug can help in determining the nature of the search and query syntax in use at the time of a low memory condition. The debug should only be used for the time of the investigation only.
The debug results in output similar to the following in the Lotus Domino console log:
[185C:000B-1D08] IN FTGSearch
option = 0x400089
[185C:000B-1D08] Query: [Field1] = "aaa*" OR [Field1] = "bbb*" OR [Field1] = "ccc*" OR [Field1] = "ddd*" OR [Field1] = "eee*" OR [Field1] = "fff*" OR [Field1] = "ggg*" OR [Field1] = "hhh*" OR [Field1] = "iii*" OR [Field1] = "jjj*"
OR [Field1] = "kkk*" OR [Field1] = "lll* [185C:000B-1D08] Engine Query: "aaa@F234" * "bbb@F234" * "ccc@F234" * "ddd@F234" * "eee@F234" * "fff@F234" * "ggg@F234" * "hhh@F234" * "iii@F234" * "jjj@F234" * "kkk@F234"
* "lll@F234" * "mmm@F234" * "nnn@F234" * "ooo@F234" * "ppp@F234" * "qqq@F234" * "rr
[185C:000B-1D08] FTGmalloc failed, error = 418, size = 44
[185C:000B-1D08]OUT FTGSearch error = F34
The truncation of the lines is expected and is only in relation to debugging. In this case, the search used several conditional statements which can impact memory when trying to attempt to search.
Resolving the problem
How to reduce a low memory condition:
Allow memory allocations directly from system memory (SPR# JSTN65HQRJ was fixed in Notes/Domino 6 to help alleviate shared memory limitations). The following notes.ini file parameters were introduced to increase full-text search performance and reduce memory consumption, particularly on big databases with many unique numeric fields. This will instruct the full-text Search engine to use direct malloc calls from the operating system for large allocations instead of requesting memory through the Notes Memory Manager.
These settings help alleviate resource limits but do not eliminate all resource restrictions. The settings are provided to the system administrator in order to improve search results, such as reducing time on high hit result sets, and to help prevent out of memory conditions. The resource consumption is directly related to the following situations:
-- The size of the database with number of documents.
-- The type and characteristics of the query with the number of unique keys.
-- The system resources available at the time (hardware and activity dependent).
How to prevent server instability for this low memory condition:
An enhancement request was created in SPR# JSTN8C52GM: Memory consumption is a limiting factor for FTI/Search, especially for area of GTR query processing. This option provides a means to monitor and control memory consumption in the GTR search engine by detect low memory conditions before they occur and allow the administrator to isolate complex searches.
FTG_CHECK_THREAD_MAX_MEM=1 enables memory detection/monitoring for GTR
FTG_MAX_MB_PER_THREAD=<megabytes> is the maximum memory in megabytes allowed for a single thread. Default is 100
FTG_MB_PER_THREAD=<megabytes> is the amount of average memory in megabytes allowed per thread, it is ONLY used as multiplied by FTG_MAX_MEM_POOL. Default is 1
FTG_MAX_MEM_POOL=<concurrent threads> - this times the FTG_MB_PER_THREAD setting is the global memory allowed across all FT searching threads concurrently. Default is 512
So 512MB is the maximum allowed globally and 100MB per thread unless things are overridden.
Enable FTG_CHECK_THREAD_MAX_MEM=1 to isolate specific queries that exceed default memory allocations allowed and report the memory consumed. This is a troubleshooting option that is recommended when a particular query is the culprit and not load in general. Also, FTG_MAX_MB_PER_THREAD=<megabytes> is the maximum memory allowed for a single thread. The default is 100MB but this may be lowered to isolate queries suspect of exhausting memory. Also, by enabling this feature, the "Show Statistics FT" command to the server console will indicate memory consumption statistics to help better monitor resource consumption.
Limit the number of sessions available for the server. Methods for controlling load and/or number of open sessions may be coordinated with to accommodate the memory available on the server. FTG_MB_PER_THREAD=<megabytes> may be based on the maximum number of active sessions allowed. This in conjunction with FTG_MAX_MEM_POOL for concurrent threads will set the global memory allowed across all FT searching threads concurrently and maximize memory resources available to each thread on a per server basis.
(NOTE): Open a PMR with Product Support to help work out the appropriate values for settings above for the specific platform. The best tuning selections can be made based on platform, OS and server load which would require additional analysis on a case by case basis.