opends/resource/config/config.ldif
@@ -20,7 +20,7 @@ # # CDDL HEADER END # # Portions Copyright 2006-2007 Sun Microsystems, Inc. # Portions Copyright 2006-2008 Sun Microsystems, Inc. # # # This file contains the primary Directory Server configuration. It must not @@ -490,6 +490,7 @@ dn: cn=Entry Caches,cn=config objectClass: top objectClass: ds-cfg-branch objectClass: ds-cfg-default-entry-cache cn: Entry Caches dn: cn=FIFO,cn=Entry Caches,cn=config opends/resource/schema/02-config.ldif
@@ -21,7 +21,7 @@ # CDDL HEADER END # # # Portions Copyright 2006-2007 Sun Microsystems, Inc. # Portions Copyright 2006-2008 Sun Microsystems, Inc. # # # This file contains the attribute type and objectclass definitions for use @@ -2177,6 +2177,11 @@ SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'OpenDS Directory Server' ) attributeTypes: ( 1.3.6.1.4.1.26027.1.1.446 NAME 'ds-cfg-cache-preload' SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE X-ORIGIN 'OpenDS Directory Server' ) objectClasses: ( 1.3.6.1.4.1.26027.1.2.1 NAME 'ds-cfg-access-control-handler' SUP top @@ -3677,3 +3682,9 @@ STRUCTURAL MUST ( ds-cfg-backend ) X-ORIGIN 'OpenDS Directory Server' ) objectClasses: ( 1.3.6.1.4.1.26027.1.2.180 NAME 'ds-cfg-default-entry-cache' SUP ds-cfg-branch STRUCTURAL MAY ds-cfg-cache-preload X-ORIGIN 'OpenDS Directory Server' ) opends/src/admin/defn/org/opends/server/admin/std/DefaultEntryCacheConfiguration.xml
New file @@ -0,0 +1,69 @@ <?xml version="1.0" encoding="utf-8"?> <!-- ! CDDL HEADER START ! ! The contents of this file are subject to the terms of the ! Common Development and Distribution License, Version 1.0 only ! (the "License"). You may not use this file except in compliance ! with the License. ! ! You can obtain a copy of the license at ! trunk/opends/resource/legal-notices/OpenDS.LICENSE ! or https://OpenDS.dev.java.net/OpenDS.LICENSE. ! See the License for the specific language governing permissions ! and limitations under the License. ! ! When distributing Covered Code, include this CDDL HEADER in each ! file and include the License file at ! trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, ! add the following below this CDDL HEADER, with the fields enclosed ! by brackets "[]" replaced with your own identifying information: ! Portions Copyright [yyyy] [name of copyright owner] ! ! CDDL HEADER END ! ! ! Portions Copyright 2008 Sun Microsystems, Inc. ! --> <adm:managed-object name="default-entry-cache" plural-name="default-entry-cache" package="org.opends.server.admin.std" xmlns:adm="http://www.opends.org/admin" xmlns:ldap="http://www.opends.org/admin-ldap" xmlns:cli="http://www.opends.org/admin-cli"> <adm:synopsis> <adm:user-friendly-name /> represents the Directory Server entry cache framework. </adm:synopsis> <adm:tag name="database" /> <adm:profile name="ldap"> <ldap:object-class> <ldap:name>ds-cfg-default-entry-cache</ldap:name> <ldap:superior>ds-cfg-branch</ldap:superior> </ldap:object-class> </adm:profile> <adm:profile name="cli"> <cli:managed-object custom="true" /> </adm:profile> <adm:property name="cache-preload" mandatory="false"> <adm:synopsis> Indicates whether or not to preload the entry cache on startup. </adm:synopsis> <adm:requires-admin-action> <adm:server-restart /> </adm:requires-admin-action> <adm:default-behavior> <adm:defined> <adm:value>false</adm:value> </adm:defined> </adm:default-behavior> <adm:syntax> <adm:boolean /> </adm:syntax> <adm:profile name="ldap"> <ldap:attribute> <ldap:name>ds-cfg-cache-preload</ldap:name> </ldap:attribute> </adm:profile> </adm:property> </adm:managed-object> opends/src/admin/defn/org/opends/server/admin/std/RootConfiguration.xml
@@ -23,7 +23,7 @@ ! CDDL HEADER END ! ! ! Portions Copyright 2007 Sun Microsystems, Inc. ! Portions Copyright 2007-2008 Sun Microsystems, Inc. ! --> <adm:root-managed-object xmlns:adm="http://www.opends.org/admin" xmlns:ldap="http://www.opends.org/admin-ldap" @@ -203,6 +203,17 @@ </cli:relation> </adm:profile> </adm:relation> <adm:relation name="default-entry-cache"> <adm:one-to-one /> <adm:profile name="ldap"> <ldap:rdn-sequence>cn=Entry Caches,cn=config</ldap:rdn-sequence> </adm:profile> <adm:profile name="cli"> <cli:relation> <cli:default-property name="cache-preload" /> </cli:relation> </adm:profile> </adm:relation> <adm:relation name="entry-cache"> <adm:one-to-many /> <adm:profile name="ldap"> opends/src/messages/messages/extension.properties
@@ -20,7 +20,7 @@ # # CDDL HEADER END # # Portions Copyright 2006-2007 Sun Microsystems, Inc. # Portions Copyright 2006-2008 Sun Microsystems, Inc. @@ -72,6 +72,21 @@ to initialize fifo entry cache: %s FATAL_ERR_SOFTREFCACHE_CANNOT_INITIALIZE_9=A fatal error occurred while \ trying to initialize soft reference entry cache: %s NOTICE_CACHE_PRELOAD_PROGRESS_START_10=Starting the entry cache preload NOTICE_CACHE_PRELOAD_PROGRESS_REPORT_11=The entry cache preload has processed \ %d entries, %d MB free heap memory available NOTICE_CACHE_PRELOAD_PROGRESS_DONE_12=The entry cache preload is complete \ with the total of %d entries processed SEVERE_WARN_CACHE_PRELOAD_INTERRUPTED_13=The entry cache preload has been \ interrupted SEVERE_ERR_CACHE_PRELOAD_COLLECTOR_FAILED_14=The entry cache preload was \ unable to complete collector processing for %s backend, and as a result \ the entry cache preload for this backend will be incomplete SEVERE_WARN_CACHE_PRELOAD_BACKEND_FAILED_15=The entry cache preload is not \ supported by %s backend, and as a result no entries from this backend will \ be preloaded onto the entry cache SEVERE_ERR_CACHE_PRELOAD_ENTRY_FAILED_16=Failed to preload %s entry onto \ the entry cache: %s MILD_ERR_EXTOP_PASSMOD_ILLEGAL_REQUEST_ELEMENT_TYPE_32=The password modify \ extended request sequence included an ASN.1 element of an invalid type: %s MILD_ERR_EXTOP_PASSMOD_CANNOT_DECODE_REQUEST_33=An unexpected error occurred \ @@ -1613,4 +1628,4 @@ SEVERE_ERR_SDTUACM_ATTR_UNINDEXED_569=The subject DN to user attribute \ certificate mapper defined in configuration entry %s references attribute \ type %s which is does not have an equality index defined in backend %s opends/src/server/org/opends/server/api/Backend.java
@@ -22,7 +22,7 @@ * CDDL HEADER END * * * Portions Copyright 2006-2007 Sun Microsystems, Inc. * Portions Copyright 2006-2008 Sun Microsystems, Inc. */ package org.opends.server.api; import org.opends.messages.Message; @@ -30,6 +30,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; @@ -210,6 +211,26 @@ /** * Retrieves the set of all DNs that are stored within this backend. * Note that this can be a slow operation depending on a particular * backend implementation and might be unsupported by some backends. * * @param storedDNs Collection to retrieve all stored DNs into. * Note that for async operation a thread-safe collection * should be used. * * @return {@code true} if all DNs stored within this backend were * successfully retrieved, or {@code false} otherwise. * * @throws UnsupportedOperationException if backend implementation * does not support this operation. */ public abstract boolean collectStoredDNs(Collection<DN> storedDNs) throws UnsupportedOperationException; /** * Indicates whether the data associated with this backend may be * considered local (i.e., in a repository managed by the Directory * Server) rather than remote (i.e., in an external repository opends/src/server/org/opends/server/backends/BackupBackend.java
@@ -22,7 +22,7 @@ * CDDL HEADER END * * * Portions Copyright 2006-2007 Sun Microsystems, Inc. * Portions Copyright 2006-2008 Sun Microsystems, Inc. */ package org.opends.server.backends; @@ -1297,5 +1297,16 @@ new AttributeValue(rdnAttrType, rdnStringValue); return parentDN.concat(RDN.create(rdnAttrType, attrValue)); } /** * {@inheritDoc} */ public boolean collectStoredDNs(Collection<DN> storedDNs) throws UnsupportedOperationException { throw new UnsupportedOperationException("Operation not supported."); } } opends/src/server/org/opends/server/backends/LDIFBackend.java
@@ -22,13 +22,14 @@ * CDDL HEADER END * * * Portions Copyright 2007 Sun Microsystems, Inc. * Portions Copyright 2007-2008 Sun Microsystems, Inc. */ package org.opends.server.backends; import java.io.File; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; @@ -1572,5 +1573,22 @@ return alerts; } /** * {@inheritDoc} */ public boolean collectStoredDNs(Collection<DN> storedDNs) throws UnsupportedOperationException { backendLock.readLock().lock(); try { storedDNs.addAll(entryMap.keySet()); return true; } finally { backendLock.readLock().unlock(); } } } opends/src/server/org/opends/server/backends/MemoryBackend.java
@@ -22,12 +22,13 @@ * CDDL HEADER END * * * Portions Copyright 2006-2007 Sun Microsystems, Inc. * Portions Copyright 2006-2008 Sun Microsystems, Inc. */ package org.opends.server.backends; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; @@ -987,5 +988,17 @@ Message message = ERR_MEMORYBACKEND_BACKUP_RESTORE_NOT_SUPPORTED.get(); throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message); } /** * {@inheritDoc} */ public synchronized boolean collectStoredDNs(Collection<DN> storedDNs) throws UnsupportedOperationException { storedDNs.addAll(entryMap.keySet()); return true; } } opends/src/server/org/opends/server/backends/MonitorBackend.java
@@ -22,13 +22,14 @@ * CDDL HEADER END * * * Portions Copyright 2006-2007 Sun Microsystems, Inc. * Portions Copyright 2006-2008 Sun Microsystems, Inc. */ package org.opends.server.backends; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; @@ -1276,5 +1277,16 @@ currentConfig = backendCfg; return new ConfigChangeResult(resultCode, adminActionRequired, messages); } /** * {@inheritDoc} */ public boolean collectStoredDNs(Collection<DN> storedDNs) throws UnsupportedOperationException { throw new UnsupportedOperationException("Operation not supported."); } } opends/src/server/org/opends/server/backends/RootDSEBackend.java
@@ -22,7 +22,7 @@ * CDDL HEADER END * * * Portions Copyright 2006-2007 Sun Microsystems, Inc. * Portions Copyright 2006-2008 Sun Microsystems, Inc. */ package org.opends.server.backends; @@ -1529,5 +1529,16 @@ return new ConfigChangeResult(resultCode, adminActionRequired, messages); } /** * {@inheritDoc} */ public boolean collectStoredDNs(Collection<DN> storedDNs) throws UnsupportedOperationException { throw new UnsupportedOperationException("Operation not supported."); } } opends/src/server/org/opends/server/backends/SchemaBackend.java
@@ -22,7 +22,7 @@ * CDDL HEADER END * * * Portions Copyright 2006-2007 Sun Microsystems, Inc. * Portions Copyright 2006-2008 Sun Microsystems, Inc. */ package org.opends.server.backends; @@ -37,6 +37,7 @@ import java.security.MessageDigest; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Date; import java.util.HashMap; import java.util.HashSet; @@ -5575,5 +5576,16 @@ return new Attribute(attributeTypesType, ATTR_ATTRIBUTE_TYPES, valueSetCopy); } /** * {@inheritDoc} */ public boolean collectStoredDNs(Collection<DN> storedDNs) throws UnsupportedOperationException { throw new UnsupportedOperationException("Operation not supported."); } } opends/src/server/org/opends/server/backends/TrustStoreBackend.java
@@ -22,7 +22,7 @@ * CDDL HEADER END * * * Portions Copyright 2007 Sun Microsystems, Inc. * Portions Copyright 2007-2008 Sun Microsystems, Inc. */ package org.opends.server.backends; @@ -42,6 +42,7 @@ import java.security.KeyStoreException; import java.security.cert.Certificate; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.LinkedHashSet; @@ -1898,5 +1899,16 @@ } } /** * {@inheritDoc} */ public boolean collectStoredDNs(Collection<DN> storedDNs) throws UnsupportedOperationException { throw new UnsupportedOperationException("Operation not supported."); } } opends/src/server/org/opends/server/backends/jeb/BackendImpl.java
@@ -22,9 +22,12 @@ * CDDL HEADER END * * * Portions Copyright 2006-2007 Sun Microsystems, Inc. * Portions Copyright 2007-2008 Sun Microsystems, Inc. */ package org.opends.server.backends.jeb; import com.sleepycat.je.Cursor; import com.sleepycat.je.CursorConfig; import com.sleepycat.je.DatabaseEntry; import org.opends.messages.Message; import java.io.IOException; @@ -39,6 +42,8 @@ import com.sleepycat.je.DatabaseException; import com.sleepycat.je.EnvironmentConfig; import com.sleepycat.je.LockMode; import com.sleepycat.je.OperationStatus; import com.sleepycat.je.RunRecoveryException; import org.opends.server.admin.std.meta.LocalDBIndexCfgDefn; @@ -67,6 +72,8 @@ import org.opends.server.admin.std.server.LocalDBBackendCfg; import org.opends.server.admin.Configuration; import org.opends.server.admin.server.ConfigurationChangeListener; import org.opends.server.protocols.asn1.ASN1OctetString; import org.opends.server.types.DN; /** * This is an implementation of a Directory Server Backend which stores entries @@ -1729,4 +1736,45 @@ throw new InitializationException(message, e); } } /** * {@inheritDoc} */ public boolean collectStoredDNs(Collection<DN> storedDNs) throws UnsupportedOperationException { for (EntryContainer entryContainer : rootContainer.getEntryContainers()) { DN2ID dn2id = entryContainer.getDN2ID(); Cursor cursor = null; try { cursor = dn2id.openCursor(null, new CursorConfig()); DatabaseEntry key = new DatabaseEntry(); DatabaseEntry data = new DatabaseEntry(); OperationStatus status; for (status = cursor.getFirst(key, data, LockMode.DEFAULT); status == OperationStatus.SUCCESS; status = cursor.getNext(key, data, LockMode.DEFAULT)) { DN entryDN = DN.decode(new ASN1OctetString(key.getData())); storedDNs.add(entryDN); } } catch (Exception e) { if (debugEnabled()) { TRACER.debugCaught(DebugLogLevel.ERROR, e); } return false; } finally { if (cursor != null) { try { cursor.close(); } catch (DatabaseException de) { if (debugEnabled()) { TRACER.debugCaught(DebugLogLevel.ERROR, de); } } } } } return true; } } opends/src/server/org/opends/server/backends/task/TaskBackend.java
@@ -22,7 +22,7 @@ * CDDL HEADER END * * * Portions Copyright 2006-2007 Sun Microsystems, Inc. * Portions Copyright 2006-2008 Sun Microsystems, Inc. */ package org.opends.server.backends.task; @@ -31,6 +31,7 @@ import java.io.File; import java.net.InetAddress; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.Iterator; import java.util.List; @@ -1544,5 +1545,16 @@ { return taskScheduler.getRecurringTask(taskEntryDN); } /** * {@inheritDoc} */ public boolean collectStoredDNs(Collection<DN> storedDNs) throws UnsupportedOperationException { throw new UnsupportedOperationException("Operation not supported."); } } opends/src/server/org/opends/server/core/EntryCacheConfigManager.java
@@ -22,7 +22,7 @@ * CDDL HEADER END * * * Portions Copyright 2006-2007 Sun Microsystems, Inc. * Portions Copyright 2006-2008 Sun Microsystems, Inc. */ package org.opends.server.core; import org.opends.messages.Message; @@ -57,6 +57,7 @@ import org.opends.server.config.ConfigConstants; import org.opends.server.config.ConfigEntry; import org.opends.server.extensions.DefaultEntryCache; import org.opends.server.extensions.EntryCachePreloader; import org.opends.server.monitors.EntryCacheMonitorProvider; import org.opends.server.types.DN; @@ -225,6 +226,13 @@ } } } // If requested preload the entry cache. if (rootConfiguration.getDefaultEntryCache().isCachePreload()) { // Kick off preload arbiter main thread. EntryCachePreloader preloadThread = new EntryCachePreloader(); preloadThread.start(); } } opends/src/server/org/opends/server/extensions/ConfigFileHandler.java
@@ -22,7 +22,7 @@ * CDDL HEADER END * * * Portions Copyright 2006-2007 Sun Microsystems, Inc. * Portions Copyright 2006-2008 Sun Microsystems, Inc. */ package org.opends.server.extensions; @@ -36,6 +36,7 @@ import java.io.OutputStream; import java.security.MessageDigest; import java.util.Arrays; import java.util.Collection; import java.util.Date; import java.util.HashMap; import java.util.HashSet; @@ -3615,5 +3616,16 @@ logError(message); } } /** * {@inheritDoc} */ public boolean collectStoredDNs(Collection<DN> storedDNs) throws UnsupportedOperationException { throw new UnsupportedOperationException("Operation not supported."); } } opends/src/server/org/opends/server/extensions/EntryCachePreloader.java
New file @@ -0,0 +1,386 @@ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at * trunk/opends/resource/legal-notices/OpenDS.LICENSE * or https://OpenDS.dev.java.net/OpenDS.LICENSE. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, * add the following below this CDDL HEADER, with the fields enclosed * by brackets "[]" replaced with your own identifying information: * Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END * * * Portions Copyright 2008 Sun Microsystems, Inc. */ package org.opends.server.extensions; import org.opends.messages.Message; import java.util.Collections; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.locks.Lock; import org.opends.server.api.Backend; import org.opends.server.api.DirectoryThread; import org.opends.server.api.ServerShutdownListener; import org.opends.server.core.DirectoryServer; import org.opends.server.types.DN; import org.opends.server.types.DebugLogLevel; import org.opends.server.types.DirectoryException; import org.opends.server.loggers.debug.DebugTracer; import org.opends.server.types.LockManager; import static org.opends.server.util.StaticUtils.*; import static org.opends.server.loggers.debug.DebugLogger.*; import static org.opends.server.loggers.ErrorLogger.logError; import static org.opends.messages.ExtensionMessages.*; /** * This class defines a utility that will be used to pre-load the Directory * Server entry cache. Pre-loader is multi-threaded and consist of the * following threads: * * - The Arbiter thread which monitors overall pre-load progress and manages * pre-load worker threads by adding or removing them as deemed necessary. * * - The Collector thread which collects all entry DNs stored within every * configured and active backend to a shared object workers consume from. * * - Worker threads which are responsible for monitoring the collector feed * and requesting the actual entries for retrieval and in cache storage. * * This implementation is entry cache and backend independent and can be * used to pre-load from any backend to any entry cache as long as both * are capable of initiating and sustaining such pre-load activity. * * This implementation is fully synchronized and safe to use with the server * online which pre-load activities going in parallel with server operations. * * This implementation is self-adjusting to any system workload and does not * require any configuration parameters to optimize for initial system * resources availability and/or any subsequent fluctuations. */ public class EntryCachePreloader extends DirectoryThread implements ServerShutdownListener { /** * The tracer object for the debug logger. */ private static final DebugTracer TRACER = getTracer(); /** * Interrupt flag for the arbiter to terminate worker threads. */ private AtomicBoolean interruptFlag = new AtomicBoolean(false); /** * Processed entries counter. */ private AtomicLong processedEntries = new AtomicLong(0); /** * Progress report resolution. */ private static final long progressInterval = 5000; /** * Default arbiter resolution time. */ public static final long PRELOAD_ARBITER_DEFAULT_SLEEP_TIME = 1000; /** * Effective arbiter resolution time. */ private static long arbiterSleepTime; /** * Pre-load arbiter thread name. */ private String preloadArbiterThreadName; /** * Pre-load arbiter thread. */ private Thread preloadArbiterThread; /** * Worker threads. */ private List<Thread> preloadThreads = Collections.synchronizedList( new LinkedList<Thread>()); /** * DN Collector thread. */ private EntryCacheDNCollector dnCollector = new EntryCacheDNCollector(); /** * This queue is for workers to take from. */ private LinkedBlockingQueue<DN> dnQueue = new LinkedBlockingQueue<DN>(); /** * The number of bytes in a megabyte. */ private static final int bytesPerMegabyte = 1024*1024; /** * Default constructor. */ public EntryCachePreloader() { super("Entry Cache Preload Arbiter"); preloadArbiterThreadName = getName(); DirectoryServer.registerShutdownListener(this); // This should not be exposed as configuration // parameter and is only useful for testing. arbiterSleepTime = Long.getLong( "org.opends.server.entrycache.preload.sleep", PRELOAD_ARBITER_DEFAULT_SLEEP_TIME); } /** * The Arbiter thread. */ @Override public void run() { preloadArbiterThread = Thread.currentThread(); logError(NOTE_CACHE_PRELOAD_PROGRESS_START.get()); // Start DN collector thread first. dnCollector.start(); // Kick off a single worker. EntryCachePreloadWorker singleWorkerThread = new EntryCachePreloadWorker(); singleWorkerThread.start(); preloadThreads.add(singleWorkerThread); // Progress report timer task. Timer timer = new Timer(); TimerTask progressTask = new TimerTask() { // Persistent state restore progress report. public void run() { if (processedEntries.get() > 0) { long freeMemory = Runtime.getRuntime().freeMemory() / bytesPerMegabyte; Message message = NOTE_CACHE_PRELOAD_PROGRESS_REPORT.get( processedEntries.get(), freeMemory); logError(message); } } }; timer.scheduleAtFixedRate(progressTask, progressInterval, progressInterval); // Cycle to monitor progress and adjust workers. long processedEntriesDeltaLow = 0; long processedEntriesDeltaHigh = 0; long lastKnownProcessedEntries = 0; try { while (!dnQueue.isEmpty() || dnCollector.isAlive()) { long processedEntriesCycle = processedEntries.get(); long processedEntriesDelta = processedEntriesCycle - lastKnownProcessedEntries; lastKnownProcessedEntries = processedEntriesCycle; // Spawn another worker if scaling up. if (processedEntriesDelta > processedEntriesDeltaHigh) { processedEntriesDeltaLow = processedEntriesDeltaHigh; processedEntriesDeltaHigh = processedEntriesDelta; EntryCachePreloadWorker workerThread = new EntryCachePreloadWorker(); workerThread.start(); preloadThreads.add(workerThread); } // Interrupt random worker if scaling down. if (processedEntriesDelta < processedEntriesDeltaLow) { processedEntriesDeltaHigh = processedEntriesDeltaLow; processedEntriesDeltaLow = processedEntriesDelta; // Leave at least one worker to progress. if (preloadThreads.size() > 1) { interruptFlag.set(true); } } Thread.sleep(arbiterSleepTime); } // Join the collector. dnCollector.join(); // Join all spawned workers. for (Thread workerThread : preloadThreads) { workerThread.join(); } // Cancel progress report task and report done. timer.cancel(); Message message = NOTE_CACHE_PRELOAD_PROGRESS_DONE.get( processedEntries.get()); logError(message); } catch (InterruptedException ex) { if (debugEnabled()) { TRACER.debugCaught(DebugLogLevel.ERROR, ex); } // Interrupt the collector. dnCollector.interrupt(); // Interrupt all preload threads. for (Thread thread : preloadThreads) { thread.interrupt(); } logError(WARN_CACHE_PRELOAD_INTERRUPTED.get()); } finally { // Kill the task in case of exception. timer.cancel(); } } /** * The worker thread. */ private class EntryCachePreloadWorker extends DirectoryThread { public EntryCachePreloadWorker() { super("Entry Cache Preload Worker"); } @Override public void run() { while (!dnQueue.isEmpty() || dnCollector.isAlive()) { // Check if interrupted. if (Thread.interrupted()) { break; } if (interruptFlag.compareAndSet(true, false)) { break; } // Dequeue the next entry DN. try { DN entryDN = dnQueue.take(); Lock readLock = null; try { // Acquire a read lock on the entry. readLock = LockManager.lockRead(entryDN); if (readLock == null) { // It is cheaper to put this DN back on the // queue then pick it up and process later. dnQueue.add(entryDN); continue; } // Even if getEntry() below fails the entry is // still treated as a processed entry anyways. processedEntries.getAndIncrement(); // getEntry() will trigger putEntryIfAbsent() to the // cache if given entry is not in the cache already. DirectoryServer.getEntry(entryDN); } catch (DirectoryException ex) { if (debugEnabled()) { TRACER.debugCaught(DebugLogLevel.ERROR, ex); } Message message = ERR_CACHE_PRELOAD_ENTRY_FAILED.get( entryDN.toNormalizedString(), (ex.getCause() != null ? ex.getCause().getMessage() : stackTraceToSingleLineString(ex))); logError(message); } finally { LockManager.unlock(entryDN, readLock); } } catch (InterruptedException ex) { break; } } preloadThreads.remove(Thread.currentThread()); } } /** * The Collector thread. */ private class EntryCacheDNCollector extends DirectoryThread { public EntryCacheDNCollector() { super("Entry Cache Preload Collector"); } @Override public void run() { Map<DN, Backend> baseDNMap = DirectoryServer.getPublicNamingContexts(); Set<Backend> proccessedBackends = new HashSet<Backend>(); // Collect all DNs from every active public backend. for (Backend backend : baseDNMap.values()) { // Check if interrupted. if (Thread.interrupted()) { return; } if (!proccessedBackends.contains(backend)) { proccessedBackends.add(backend); try { if (!backend.collectStoredDNs(dnQueue)) { // DN collection is incomplete, likely // due to some backend problem occured. // Log an error message and carry on. Message message = ERR_CACHE_PRELOAD_COLLECTOR_FAILED.get( backend.getBackendID()); logError(message); } } catch (UnsupportedOperationException ex) { // Some backends dont have collectStoredDNs() // method implemented, log a warning, skip // such backend and continue. if (debugEnabled()) { TRACER.debugCaught(DebugLogLevel.ERROR, ex); } Message message = WARN_CACHE_PRELOAD_BACKEND_FAILED.get( backend.getBackendID()); logError(message); } } } } } /** * {@inheritDoc} */ public String getShutdownListenerName() { return preloadArbiterThreadName; } /** * {@inheritDoc} */ public void processServerShutdown(Message reason) { if ((preloadArbiterThread != null) && preloadArbiterThread.isAlive()) { // Interrupt the arbiter so it can interrupt // the collector and all spawned workers. preloadArbiterThread.interrupt(); try { // This should be quick although if it // gets interrupted it is no big deal. preloadArbiterThread.join(); } catch (InterruptedException ex) { if (debugEnabled()) { TRACER.debugCaught(DebugLogLevel.ERROR, ex); } } } DirectoryServer.deregisterShutdownListener(this); } } opends/src/server/org/opends/server/replication/server/ReplicationBackend.java
@@ -22,7 +22,7 @@ * CDDL HEADER END * * * Portions Copyright 2007 Sun Microsystems, Inc. * Portions Copyright 2007-2008 Sun Microsystems, Inc. */ package org.opends.server.replication.server; import static org.opends.messages.BackendMessages.*; @@ -40,6 +40,7 @@ import java.io.IOException; import java.io.StringReader; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -1325,5 +1326,16 @@ return new LDIFReader(config); } } /** * {@inheritDoc} */ public boolean collectStoredDNs(Collection<DN> storedDNs) throws UnsupportedOperationException { throw new UnsupportedOperationException("Operation not supported."); } } opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/PreloadEntryCacheTestCase.java
New file @@ -0,0 +1,235 @@ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at * trunk/opends/resource/legal-notices/OpenDS.LICENSE * or https://OpenDS.dev.java.net/OpenDS.LICENSE. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, * add the following below this CDDL HEADER, with the fields enclosed * by brackets "[]" replaced with your own identifying information: * Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END * * * Portions Copyright 2008 Sun Microsystems, Inc. */ package org.opends.server.extensions; import java.io.File; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.ArrayList; import org.opends.server.TestCaseUtils; import org.opends.server.admin.server.AdminTestCaseUtils; import org.testng.annotations.BeforeClass; import org.opends.server.admin.std.meta.*; import org.opends.server.admin.std.server.EntryCacheCfg; import org.opends.server.admin.std.server.FileSystemEntryCacheCfg; import org.opends.server.core.DirectoryServer; import org.opends.server.types.Entry; import org.opends.server.util.ServerConstants; import org.testng.annotations.AfterClass; import org.testng.annotations.Test; import static org.testng.Assert.*; /** * The entry cache pre-load test class. */ @Test(groups = { "entrycache", "slow" }, sequential=true) public class PreloadEntryCacheTestCase extends ExtensionsTestCase { /** * Number of unique dummy test entries. */ protected int NUMTESTENTRIES = 1000; /** * Dummy test entries. */ protected ArrayList<Entry> testEntriesList; /** * Entry cache configuration instance. */ protected EntryCacheCfg configuration; /** * Initialize the entry cache pre-load test. * * @throws Exception If an unexpected problem occurs. */ @BeforeClass() @SuppressWarnings("unchecked") public void preloadEntryCacheTestInit() throws Exception { // Ensure that the server is running. TestCaseUtils.startServer(); // Initialize a backend with a base entry. TestCaseUtils.clearJEBackend(true, "userRoot", "dc=example,dc=com"); // Configure the entry cache, use FileSystemEntryCache. Entry cacheConfigEntry = TestCaseUtils.makeEntry( "dn: cn=File System,cn=Entry Caches,cn=config", "objectClass: ds-cfg-file-system-entry-cache", "objectClass: ds-cfg-entry-cache", "objectClass: top", "cn: File System", "ds-cfg-cache-level: 1", "ds-cfg-java-class: " + "org.opends.server.extensions.FileSystemEntryCache", "ds-cfg-enabled: true"); configuration = AdminTestCaseUtils.getConfiguration( EntryCacheCfgDefn.getInstance(), cacheConfigEntry); // Make some dummy test entries. testEntriesList = new ArrayList<Entry>(NUMTESTENTRIES); for(int i = 0; i < NUMTESTENTRIES; i++ ) { Entry testEntry = TestCaseUtils.makeEntry( "dn: uid=test" + Integer.toString(i) + ".user" + Integer.toString(i) + ",dc=example,dc=com", "objectClass: person", "objectClass: inetorgperson", "objectClass: top", "objectClass: organizationalperson", "postalAddress: somewhere in Testville" + Integer.toString(i), "street: Under Construction Street" + Integer.toString(i), "l: Testcounty" + Integer.toString(i), "st: Teststate" + Integer.toString(i), "telephoneNumber: +878 8378 8378" + Integer.toString(i), "mobile: +878 8378 8378" + Integer.toString(i), "homePhone: +878 8378 8378" + Integer.toString(i), "pager: +878 8378 8378" + Integer.toString(i), "mail: test" + Integer.toString(i) + ".user" + Integer.toString(i) + "@testdomain.net", "postalCode: 8378" + Integer.toString(i), "userPassword: testpassword" + Integer.toString(i), "description: description for Test" + Integer.toString(i) + "User" + Integer.toString(i), "cn: Test" + Integer.toString(i) + "User" + Integer.toString(i), "sn: User" + Integer.toString(i), "givenName: Test" + Integer.toString(i), "initials: TST" + Integer.toString(i), "employeeNumber: 8378" + Integer.toString(i), "uid: test" + Integer.toString(i) + ".user" + Integer.toString(i) ); testEntriesList.add(testEntry); TestCaseUtils.addEntry(testEntry); } // Initialize the cache reflecting on DirectoryServer // and EntryCacheConfigManager. final Field[] directoryFields = DirectoryServer.class.getDeclaredFields(); for (int i = 0; i < directoryFields.length; ++i) { if (directoryFields[i].getName().equals("entryCacheConfigManager")) { directoryFields[i].setAccessible(true); final Method[] cacheManagerMethods = directoryFields[i].getType().getDeclaredMethods(); for (int j = 0; j < cacheManagerMethods.length; ++j) { if (cacheManagerMethods[j].getName().equals( "loadAndInstallEntryCache")) { cacheManagerMethods[j].setAccessible(true); cacheManagerMethods[j].invoke(directoryFields[i].get( DirectoryServer.getInstance()), configuration.getJavaClass(), configuration); } } } } // Attempt to force GC to possibly free some memory. System.gc(); } /** * Tests the entry cache pre-load. */ @Test() public void testEntryCachePreload() throws Exception { // Make sure the entry cache is empty. assertNull(toVerboseString(), "Expected empty cache. " + "Cache contents:" + ServerConstants.EOL + toVerboseString()); // Start pre-load and wait for it to complete. EntryCachePreloader preloadThread = new EntryCachePreloader(); preloadThread.start(); preloadThread.join(); // Check that all test entries are preloaded. for(int i = 0; i < NUMTESTENTRIES; i++ ) { assertNotNull(DirectoryServer.getEntryCache().getEntry( testEntriesList.get(i).getDN()), "Expected to find " + testEntriesList.get(i).getDN().toString() + " in the cache. Cache contents:" + ServerConstants.EOL + toVerboseString()); } } /** * Finalize the entry cache pre-load test. * * @throws Exception If an unexpected problem occurs. */ @AfterClass() public void preloadEntryCacheTestFini() throws Exception { // Clear backend. TestCaseUtils.clearJEBackend(false, "userRoot", "dc=example,dc=com"); // Sanity in-core restart. TestCaseUtils.restartServer(); // Remove default FS cache JE environment. FileSystemEntryCacheCfg config = (FileSystemEntryCacheCfg) configuration; TestCaseUtils.deleteDirectory(new File(config.getCacheDirectory())); } /** * Reflection of the toVerboseString implementation method. */ protected String toVerboseString() throws Exception { final Method[] cacheMethods = DirectoryServer.getEntryCache().getClass().getDeclaredMethods(); for (int i = 0; i < cacheMethods.length; ++i) { if (cacheMethods[i].getName().equals("toVerboseString")) { cacheMethods[i].setAccessible(true); Object verboseString = cacheMethods[i].invoke(DirectoryServer.getEntryCache(), (Object[]) null); return (String) verboseString; } } return null; } }