This page contains a variety of Lotus Notes and Domino related performance tips, with links to additional information where possible. In particular, you should visit the links in the "Required Reading" section of this page. Those pages and documents have a number of performance tips beyond what I mention here.
This is not supposed to be an exhaustive list by any means. Rather, it's intended to be a quick reference of things to keep in mind as you're designing a Notes application or setting up a Domino server. I also avoided giving any "step-by-step" instructions on this page. If you need any help understanding the concepts I talk about here, the first place you should look is the Notes help files (both Domino Designer help and Domino Administrator help). If those don't give you enough information, please try searching the LDD forums.
For my general feelings about optimizing programs, please see my blog entry on the subject: 12 Thoughts About Making Code More Efficient.
Subdivisions of this Page
Domino Server Performance Troubleshooting Cookbook (IBM Technote)
Domino 7 Performance Tuning Best Practices (IBM Redpaper)
Good tips in the classic Maximizing Domino Performance White Paper
Performance Considerations for Domino Applications
Some good Appendices in that Redbook:
Tuning IBM xSeries Servers for Performance
Redbook with good generic information about Win2k and Domino tuning, not just on xSeries. One of the more interesting things they mention is that Domino works best with a disk stripe size of 16KB.
Rob McDonagh's e-Pro articles on improving Domino Performance:
part 1 and part 2
All of Julie Kadashevich's Articles on LDD
LDD Performance Links
LDD "Performance Perspectives" Articles
Domino Performance-Related Redbooks
Not strictly about performance tuning, but there's a good technical article on the IBM site called Sizing your IBM Lotus Domino mail servers
Minimize Deletion Stubs
In a database that gets archived frequently, or that has a large number of "temporary" documents that get removed on a scheduled basis, you can keep your database lean by reducing the number of deletion stubs it maintains.
You can use the "Remove Documents Not Modified in Last X Days" setting in the database replication options to minimize deletion stubs in databases with a lot of deletions. Deletion stubs get purged at about one half the number of days specified in this setting, regardless of whether or not the setting is active (normally you do not want this setting to be active, but the number of days affects the deletion stub purge interval regardless).
Be Mindful of the Advanced Database Options in R5 and Higher
Notes R5 introduced a number of "advanced" database options (available by going to the last tab on the "Database Properties" dialog). Some of these options can affect the performance of your database if they're selected but not needed. The options are:
For guidance, see Properties that improve database performance in Notes Designer Help, and Chapter 4 of Performance Considerations for Domino Applications.
Adjust Full-Text Index Options
Adjust the frequency of full-text index updates on large databases with many changes (set it to hourly or scheduled, if the database doesn't use the index very much). Also, skip indexing attachments unless necessary. For large or volitile databases this can alleviate work by the Indexer task.
Obviously, if you don't need to maintain an index on a database, you shouldn't.
Define Anonymous in the ACL
This is a pretty small performance gain, but you should always explicitly define the "Anonymous" user access in the ACL of a web-enabled database. It's much more efficient for the database to find the Anonymous user directly in the ACL, instead of having to do a nested lookup through all the groups in the ACL to see if Anonymous happens to be defined there (especially if you're using LDAP). It also makes it easier to control the web access to your databases right from the start.
Limit Use of "Strong Encryption"
Don't use "Strong Encryption" on databases unless absolutely necessary, especially on high-use server-based databases. It adds a lot of overhead when the client has to keep translating everything in the database. If you must use database encryption, Medium and Low encryption have much less overhead. And you normally shouldn't have to encrypt a server-based database at all.
Minimize the Use of Reader Name Fields When Possible
Using reader name fields on many or all documents in a database with a lot of documents can significantly affect view performance, because the database has to evaluate the reader fields on a number of documents before it can even display a view. There's a good article on the problem and possible solutions on the LDD site.
In short, you should Categorize all your views if you're using Reader fields extensively on a database with many documents (and make sure that "Collapse all when database is first opened" is checked, as it always should be). In ND6, I think you also need to have the "Don't Show Empty Categories" option turned off for this to be effective.
In R5 and higher, you can also use "single category" displays of the view on a Page or Form to help even further. In ND6 and higher, you can use the @SetViewInfo command to dynamically filter a view.
Watch Out for Private Views Stored on the Server
If you have Private on First Use views that are stored on the server, you can spike the server utilization if (A) the indexes are supposed to be refreshed automatically, (B) a lot of users have private views stored on the server, and (C) the database has many documents and gets changed often. This is because every time a change is made, the server will have to reindex all the private views (which could be in the hundreds for many users). The problem can be worse if the view and/or column formulas are complex.
There are also INI settings that will delay index updates, to temporarily alleviate this problem server-wide without having to delete and redesign existing private views (see the Maximizing Domino Performance White Paper for details)
Don't Use @Now or @Today in Views
Don't use @Now or @Today in a view selection or view column formulas if you can help it. This can cause views to be refreshed almost constantly whenever databases get changed or replicated, which can be a big problem on databases with a lot of changes. There is a lot of discussion about working around this issue on the LDD forums.
Create Special Views for Lookups
If you need to create one or more views for looking up data, use hidden views with few columns, no categorization, minimal sorting, and no complex formulas. The key is that you want your lookup views to have as little data (or data manipulation) as possible, and they should reindex very quickly.
Check Your View Index Sizes
For server-based databases, you can use the "show database [dbname]" command at the server console to see how big the view indexes are. This can come in handy when trying to determine how complex your views really are, and what kind of impact they have on the database (you may be quite surprised at the index sizes on large databases with a lot of categorized views).
Evaluate Formulas Only Once
If many elements on your form rely on the same formula for lookups or hide-when fields, just process the formula once in a single computed (or computed for display) field and use that computed field value for the lookup/hide-when.
Along those same lines, if you have a large section of a form that needs to be hidden under a certain condition, consider using a computed subform instead of using a hide-when formula for all the different paragraphs.
(UPDATE: Andre Guirard wrote an interesting analysis of when using computed fields for this is and isn't a good idea, and also mentions that you can use shared profile documents as a way to improve speed for keyword lookups.)
Avoid WebQueryOpen Agents Whenever Possible
WebQueryOpen agents are notoriously slow, and even simple WebQueryOpen agents can cause a noticable delay in opening a form on the web. Avoid these when possible.
Avoid Large or Complex Tables
Large or complex tables can greatly affect the speed with which a form can be rendered (both in the client and in the browser). In particular, avoid tables with a huge number of rows, a lot of nesting, or a large number of hide-when conditions.
Learn How to Troubleshoot Agents
Before you do anything, you need to read Julie Kadashevich's article: Troubleshooting Agents in Notes/Domino 5 and 6. This has some excellent troubleshooting tips for server-based agents.
Avoid Reader Fields When Possible
Using Reader fields can slow down all of your document processing code across the board, because the database has to evaluate the reader fields for each document before it can even attempt to do anything with them. This is especially true when the agent signer doesn't have access to a large number of documents in the database, because the documents that aren't visible still have to be evaluated before they can be skipped.
Tips on Stepping Through Views
You often have to write code that "steps through" the documents in a view in order to get something done. Here are some pointers for when you have to do that:
Turn Off Remote Agent Debugging in ND6
Notes/Domino version 6 and higher gave you the ability to enable remote agent debugging. While this can be useful as a troubleshooting tool, you should make sure it's turned off when you're not testing with it. Remote debugging can cause agents to run much more slowly on the server.
Keep an Eye on Agent Schedules
Reduce the number of scheduled agents that run every 5 minutes or "When new documents arrive". For long-running agents that run once a day, don't schedule them at the same time, and don't run them in the middle of the day (when you can help it) or when server backups are taking place, or when nightly maintenance tasks (like Compact or Updall) are taking place.
If you want a quick snapshot of how many agents are currently scheduled on your server, enter a
"tell amgr schedule" command at the server console.
If you're working with a large number of NotesDocuments, don't create an array or list of NotesDocument objects -- this uses a lot of memory. Either use a DocumentCollection/NotesDocumentCollection or create an array/list of NoteIDs and access the documents as needed using the NoteIDs.
Share NotesView References
If you need to use a reference to a view multiple times in your code, get the view only once and share the reference (either using a global or static variable, or by passing a NotesView object as a parameter in functions/subs/methods). Accessing views using getView is a very expensive operation.
Use Profile Documents Instead of Environment Variables
You should generally prefer profile documents to environment variables to store and retrieve user-specific information in a database. It's much more efficient (doesn't require file-system access), and the user information can stay persistent across mutiple workstations.
Use @Function Agents For Simple Document Modifications
If you just need to modify, add, or delete one or several [non rich-text] fields in all or selected documents in a view, it is much faster to use an agent with simple @Functions (rather than LotusScript or Java) that runs against all or selected documents.
Don't Use "Print" So Much
Minimize the use of the Print statement when performing actions in long loops. If you feel obliged to inform the user of your progress, use something like:
count = count + 1 If (count Mod 1000) = 0 Then Print "Processed " & count & " docs" End If
to show what's going on. Printing a lot of messages to the status bar or console uses a surprising amount of memory, and it can have a noticible impact on an otherwise fast-running loop. Specifically, the memory used by each Print statement is not released until the entire agent is finished (see IBM Technote 1089676 for details).
Declare Your Variables
Declare all your variables -- and use something other than "Variant" when possible. If you don't declare your variables, then LotusScript assumes everything is a Variant, which is not only a 16-bit variable (which is large in variable terms) but it has to be internally cast every time you want to access or compare it. For example, the loop:
For i = 0 to 10000 '** do stuff Next
runs much faster when "i" is declared as an Integer or Long rather that a Variant.
Resume Out of Your Error Handling Blocks
Don't make your LotusScript error handling blocks GoTo another block unless that other block cleans up and exits the current routine. This is because the error handling block and everything that happens after it until you Resume or Exit actually operates within its own memory space. This can cause stack overflows if there are lots of errors and you end up in the error block multiple times (per Lotusphere session AD104: LotusScript Tips and Tricks)
Resize Arrays Infrequently
ReDim your arrays as infrequently as possible. However, if you have to append data to an array quite often, Redim Preserve is MUCH more efficient than many calls to ArrayAppend:
Dim arr As Variant Redim arr(0) As Integer For i% = 0 To 30000 '** Redim Preserve takes under a second Redim Preserve arr(i%) arr(i%) = i% '** Arrayappend takes almost a full minute 'arr = Arrayappend(arr, i%) Next
Try running the code above with and without Arrayappend and you'll see what I mean.
Don't Worry About NotesSession Objects
Don't worry about using:
Dim session as New NotesSession Set db = session.CurrentDatabase
in several places in your agents. The NotesSession and CurrentDatabase objects are static, so you can make those two calls as often as you'd like within your code without the performance penalty of creating a new object.
Store Values in Variables Instead of Using Extended Syntax Repeatedly
If you have to access an extended syntax value repeatedly, it's often more efficient to store the value in a variable first, and just keep accessing the variable. For example, the Designer Help says to use:
Dim views as Variant views = db.Views Forall view In views '** do stuff Next
Forall view In db.Views '** do stuff Next
if you're stepping through all the views in a database.
For Large Numbers Of String Concatenations, Use Join On An Array Of Strings
If you have to do many repeated string concatenations, you're generally better off storing all of the string fragments in a String array and then joining them all together. Notes 6 introduced the native Join function, which is a very fast way of doing this, although there are still ways you can add some efficiency in R5 and lower. See my LotusScript StringBuffer Class for an example.
Use Generic Java Performance Techniques
When you're optimizing your Java code for Notes/Domino, the first thing to do is to consider the same performance and optimization techniques that are used for generic Java code (like using StringBuffers instead of String concatenation, using Buffered streams and readers when possible, etc.). I won't even try to summarize those types of techniques here; rather, I'll point you to a few web sites that are good references:
Java Performance Tuning site:
Jonathan Hardwick's Java Optimization site (old information, but still useful): http://www.cs.cmu.edu/~jch/java/optimization.html
Java Performance Tips on the Sun site:
Java Urban Performance Legends:
Java theory and practice: Garbage collection and performance
Excellent article on the cost of throwing exceptions, with some good links at the end:
Discussions of the performance implications of nullifying objects:
PreciseJava.com (some good tips, but make sure you verify for yourself -- in particular, I did some testing with file buffering at one point and my results didn't match theirs):
Articles on Notes-Specific Java Performance Tips
Here are some articles and discussions about Java performance that are Notes-specific:
IBM Technote about calling recycle():
Colin Pretorius' findings about the need to recycle() when stepping through a DocumentCollection:
Make sure you use recycle() if you make a call to EmbeddedObject.getInputStream:
Some warnings about being too liberal in your use of recycle():
Andrew Pollack's testing of the performance implications of reusing Sessions:
How to reuse Domino Sessions in Java Servlets:
Nik Shenoy's comments about using static objects across multiple agents in Notes:
How to use AgentRunner to debug your Java agents:
Don't Use "Print" So Much
Minimize the use of System.out.print. If you must output a large amount of data to the console, consider accumulating the data in a StringBuffer and dumping it periodically, or using something like log4j.
Consider Using Servlets
High volume agents that are triggered via HTTP might have better performance as servlets rather than agents. It's not always easy to tell when this will be true, but it's worth a look if performance is a problem.
Read the Server Performance Technote
Read through and understand the Domino Server Performance Troubleshooting Cookbook -- an IBM Technote with a great compilation of information.
Remove Unnecessary Server Tasks, and Tune the Ones that are Left
This is about the first thing everyone will tell you to do, but I'll go ahead and say it anyway. Check the server tasks that run either at startup or on a scheduled basis, and remove the ones you don't need. For example, an application server probably doesn't need to be running Chronos, POP3, IMAP, or LDAP.
With the tasks that are left, make sure they're properly tuned. Do you have an optimal number of Agent Manager or Replicator tasks running? Do you have multiple Mail.Box databases on your high volume mail servers? Is Updall or Compact still running when the users arrive in the morning? Make sure that your nightly maintenance tasks (Updall, Fixup, etc) aren't running while the server is being backed up at night -- that can corrupt your backups and cause the tasks to either fail or run very slowly.
Use Your Drives and Partitions Wisely
Make sure that the server is partitioned in a way that suits your applications. For example:
Don't Virus-Scan Your Databases
Make sure that the virus scanner on your server is set to exclude your database files. Your databases are large and they change quite often, and a virus scanner that's trying to check those files every single time they're accessed or changed can kill your performance.
Some file extensions you might want to consider excluding are: .nsf, .ntf, .ndk, .box, .ndx, .dtf, or anything in a .ft folder.
Index Your Views on a Fast Drive with Lots of Space
You can use the VIEW_REBUILD_DIR setting in the Notes.ini file to specify which drive and directory Domino should use for temporary file creation as it's building view indexes. You should use a fast drive with lots of space.
Use a Fixed Swap File Size
By default, Windows (and maybe Linux) will set your swap file to be a minimum size, and allow it to grow as necessary. This can cause swap file fragmentation, and it's really unnecessary overhead for the OS to have to keep adjusting the swap file size. You're almost always better off specifying the same [large] number for the minimum and maximum size of your swap file, so the OS can use a fixed area on the hard drive for virtual memory management.
If you can, it's also good to defragment the drive your swap file will be on before you make this setting, so the OS can use a contiguous space for the file.
If You Want to Log HTTP, Log to a Text File
If you're logging HTTP access, use text logging instead of logging to a database. You can parse and view the text log files using any number of free and commercial products, and you'll avoid all the overhead of the domlog database.
In order to prevent the files from taking up too much space on your hard drive, you should set the log files to be recreated daily, and use the log file zipping technique in The Unfinished LotusScript Book to keep the old files organized and compressed.
Old Web Caching Tricks May Not Work Anymore
Be aware that many 4.6/R5 server web caching tricks were at least partially if not completely disabled in ND6. It probably won't hurt if you still employ them in your databases, but they may not function the way you think they will when you upgrade to ND6 or higher. There's a good explanation of this on the LDD ND6 forum, along with an IBM Technote on the subject.
Use the Latest ODS Version
Make sure that all the ODS versions of the databases on your server are at the same level as your server (don't worry, older clients will still be able to access them, and older servers will still be able to replicate). This is almost always more efficient.
Fine Tune Your Address Book for LDAP
For servers receiving LDAP requests, there are a few things you can do to make the incoming LDAP request processing a little more efficient:
If you're specifically having problems with LDAP, try some of the links on the Domino Directory FAQ.
Disable Reverse DNS Lookups
There's a setting in the server document that enables reverse DNS lookups -- this setting should be disabled. Trying to do a reverse lookup on all the clients that are hitting the server will only slow things down.
Use Network Compression
If your servers are running ND6 or higher, consider using server network compression when possible.
Use Transaction Logging
Using transaction logging can improve the performance of all the databases on your server (especially the high-activity ones), because it allows the server to batch-process all the database updates under certain conditions. See the Domino server administration help for more information.
If you haven't read it yet, you should take a look at the Tuning IBM xSeries Servers for Performance redbook. It's got an entire chapter on setting up Windows NT/2000 servers for Domino.
Only Use TCP/IP
By default, Windows Domino servers are sometimes set up with networking for both TCP/IP and NetBios. You should really only use the TCP/IP protocol on your servers, and disable the NetBios port if it's set (some security people would urge you to disable NetBios completely on your Windows servers, Domino or not).
Disable Services You're Not Using
Check the services that are started on your server, and make sure there isn't anything running that you don't really need. Indexer is a big one that should be disabled. You can also disable things like IIS and proxy server if they're not necessary.
Page File Settings on NT
Instead of repeating it here, I'll just point you to an LDD post that I made long ago with my thoughts on Windows page files:
Here are some general links for running Domino on Linux:
Anthony Barker's Domino/Linux articles
Understanding Domino for Linux Whitepaper
Lotus Domino 6 for Linux Redbook
Lotus Domino R5 for Linux Redbook
Performance Tuning Domino on RedHat 7.1
Also, just a side-note: I've heard before that a very common performance/scalability problem with Domino on Linux has to do with the number of file handles available on the server (both for root and the Domino server user). You can use the "ulimit" command to adjust this. There should be information on this in at least one of the links referenced above. Otherwise, search the LDD forums.
Make Startup Faster
Here are a few things you can do to make the Notes client startup faster:
Use Network Compression
If both your client and your server are running ND6 or higher, you can enable network compression to speed up communications. Note that this has to be enabled on both the client and the server.
Use CLIENT_CLOCK to Troubleshoot Database Performance
There is a Notes.ini file setting called CLIENT_CLOCK that shows what kind of Notes RPC traffic is going on between client and server when you're performing operations in a database. This can be very useful when trying to determine exactly what's taking so long when you're doing specific things in a database (like opening the database itself or creating a new document). The available settings are:
; enable NRPC output at the client CLIENT_CLOCK=1 ; show NRPC traffic in a console window DEBUG_CONSOLE=1 ; output NRPC traffic to a text file DEBUG_OUTFILE=c:\nrpc.txt
See Appendix B-8 of the Performance Considerations for Domino Applications redbook for more information on these settings. There's also a SERVER_CLOCK setting to do this sort of debugging at the server, but you probably don't want to use that -- lots and lots of logging.