1 /*-
2  * See the file LICENSE for redistribution information.
3  *
4  * Copyright (c) 2002, 2014 Oracle and/or its affiliates.  All rights reserved.
5  *
6  */
7 package com.sleepycat.je.dbi;
8 
9 import static com.sleepycat.je.dbi.DbiStatDefinition.ENVIMPL_CREATION_TIME;
10 import static com.sleepycat.je.dbi.DbiStatDefinition.ENVIMPL_RELATCHES_REQUIRED;
11 import static com.sleepycat.je.dbi.DbiStatDefinition.ENV_GROUP_DESC;
12 import static com.sleepycat.je.dbi.DbiStatDefinition.ENV_GROUP_NAME;
13 import static com.sleepycat.je.dbi.DbiStatDefinition.THROUGHPUT_GROUP_DESC;
14 import static com.sleepycat.je.dbi.DbiStatDefinition.THROUGHPUT_GROUP_NAME;
15 
16 import java.io.File;
17 import java.io.IOException;
18 import java.io.PrintStream;
19 import java.io.PrintWriter;
20 import java.io.StringWriter;
21 import java.util.ArrayList;
22 import java.util.Arrays;
23 import java.util.Collection;
24 import java.util.Comparator;
25 import java.util.Enumeration;
26 import java.util.List;
27 import java.util.NavigableSet;
28 import java.util.Properties;
29 import java.util.SortedSet;
30 import java.util.concurrent.atomic.AtomicInteger;
31 import java.util.concurrent.locks.ReentrantReadWriteLock;
32 import java.util.logging.ConsoleHandler;
33 import java.util.logging.FileHandler;
34 import java.util.logging.Formatter;
35 import java.util.logging.Handler;
36 import java.util.logging.Level;
37 import java.util.logging.Logger;
38 
39 import com.sleepycat.je.CacheMode;
40 import com.sleepycat.je.CacheModeStrategy;
41 import com.sleepycat.je.CheckpointConfig;
42 import com.sleepycat.je.CustomStats;
43 import com.sleepycat.je.Database;
44 import com.sleepycat.je.DatabaseException;
45 import com.sleepycat.je.DbInternal;
46 import com.sleepycat.je.Environment;
47 import com.sleepycat.je.EnvironmentConfig;
48 import com.sleepycat.je.EnvironmentFailureException;
49 import com.sleepycat.je.EnvironmentLockedException;
50 import com.sleepycat.je.EnvironmentMutableConfig;
51 import com.sleepycat.je.EnvironmentNotFoundException;
52 import com.sleepycat.je.EnvironmentStats;
53 import com.sleepycat.je.ExceptionListener;
54 import com.sleepycat.je.LockStats;
55 import com.sleepycat.je.OperationFailureException;
56 import com.sleepycat.je.PreloadConfig;
57 import com.sleepycat.je.PreloadStats;
58 import com.sleepycat.je.PreloadStatus;
59 import com.sleepycat.je.ProgressListener;
60 import com.sleepycat.je.RecoveryProgress;
61 import com.sleepycat.je.ReplicaConsistencyPolicy;
62 import com.sleepycat.je.StatsConfig;
63 import com.sleepycat.je.Transaction;
64 import com.sleepycat.je.TransactionConfig;
65 import com.sleepycat.je.TransactionStats;
66 import com.sleepycat.je.TransactionStats.Active;
67 import com.sleepycat.je.VerifyConfig;
68 import com.sleepycat.je.VersionMismatchException;
69 import com.sleepycat.je.cleaner.Cleaner;
70 import com.sleepycat.je.cleaner.UtilizationProfile;
71 import com.sleepycat.je.cleaner.UtilizationTracker;
72 import com.sleepycat.je.config.EnvironmentParams;
73 import com.sleepycat.je.dbi.SortedLSNTreeWalker.TreeNodeProcessor;
74 import com.sleepycat.je.dbi.StartupTracker.Phase;
75 import com.sleepycat.je.evictor.Evictor;
76 import com.sleepycat.je.incomp.INCompressor;
77 import com.sleepycat.je.latch.Latch;
78 import com.sleepycat.je.latch.LatchFactory;
79 import com.sleepycat.je.latch.LatchSupport;
80 import com.sleepycat.je.log.FileManager;
81 import com.sleepycat.je.log.LogEntryHeader;
82 import com.sleepycat.je.log.LogEntryType;
83 import com.sleepycat.je.log.LogItem;
84 import com.sleepycat.je.log.LogManager;
85 import com.sleepycat.je.log.ReplicationContext;
86 import com.sleepycat.je.log.Trace;
87 import com.sleepycat.je.log.entry.LogEntry;
88 import com.sleepycat.je.log.entry.SingleItemEntry;
89 import com.sleepycat.je.log.entry.TraceLogEntry;
90 import com.sleepycat.je.recovery.Checkpointer;
91 import com.sleepycat.je.recovery.RecoveryInfo;
92 import com.sleepycat.je.recovery.RecoveryManager;
93 import com.sleepycat.je.recovery.VLSNRecoveryProxy;
94 import com.sleepycat.je.statcap.EnvStatsLogger;
95 import com.sleepycat.je.statcap.StatCapture;
96 import com.sleepycat.je.statcap.StatCaptureDefinitions;
97 import com.sleepycat.je.statcap.StatManager;
98 import com.sleepycat.je.tree.BIN;
99 import com.sleepycat.je.tree.BINReference;
100 import com.sleepycat.je.tree.IN;
101 import com.sleepycat.je.tree.LN;
102 import com.sleepycat.je.tree.Node;
103 import com.sleepycat.je.tree.dupConvert.DupConvert;
104 import com.sleepycat.je.txn.LockType;
105 import com.sleepycat.je.txn.LockUpgrade;
106 import com.sleepycat.je.txn.Locker;
107 import com.sleepycat.je.txn.ThreadLocker;
108 import com.sleepycat.je.txn.Txn;
109 import com.sleepycat.je.txn.TxnManager;
110 import com.sleepycat.je.util.DbBackup;
111 import com.sleepycat.je.utilint.AtomicLongStat;
112 import com.sleepycat.je.utilint.DbLsn;
113 import com.sleepycat.je.utilint.LoggerUtils;
114 import com.sleepycat.je.utilint.LongStat;
115 import com.sleepycat.je.utilint.StatDefinition;
116 import com.sleepycat.je.utilint.StatGroup;
117 import com.sleepycat.je.utilint.TestHook;
118 import com.sleepycat.je.utilint.TestHookExecute;
119 import com.sleepycat.je.utilint.ThroughputStatGroup;
120 import com.sleepycat.je.utilint.TracerFormatter;
121 import com.sleepycat.je.utilint.VLSN;
122 
123 /**
124  * Underlying Environment implementation. There is a single instance for any
125  * database environment opened by the application.
126  */
127 public class EnvironmentImpl implements EnvConfigObserver {
128 
129     /*
130      * Set true and run unit tests for NO_LOCKING_MODE test.
131      * EnvironmentConfigTest.testInconsistentParams will fail. [#13788]
132      */
133     private static final boolean TEST_NO_LOCKING_MODE = false;
134 
135     /* Attributes of the entire environment */
136     private volatile DbEnvState envState;
137     private volatile boolean closing;// true if close has begun
138     private final File envHome;
139     private final AtomicInteger openCount = new AtomicInteger(0);
140     // count of open environment handles
141     private final AtomicInteger backupCount = new AtomicInteger(0);
142     // count of in-progress dbBackup
143     private boolean isTransactional; // true if env opened with DB_INIT_TRANS
144     private boolean isNoLocking;     // true if env has no locking
145     private boolean isReadOnly;   // true if env opened with the read only flag.
146     private boolean isMemOnly;       // true if je.log.memOnly=true
147     private boolean sharedCache;     // true if je.sharedCache=true
148     /* true if offset tracking should be used for deferred write dbs. */
149     private boolean dbEviction;
150 
151     private boolean allowBlindOps = false;
152     private boolean allowBlindPuts = false;
153 
154     private CacheMode cacheMode;
155     private CacheModeStrategy cacheModeStrategy;
156 
157     /* Whether or not initialization succeeded. */
158     private boolean initializedSuccessfully = false;
159 
160     /*
161      * Represents whether this environment needs to be converted from
162      * standalone to replicated.
163      */
164     protected boolean needRepConvert = false;
165 
166     private MemoryBudget memoryBudget;
167     private static int adler32ChunkSize;
168 
169     /* Save so we don't have to look it up in the config manager frequently. */
170     private long lockTimeout;
171     private long txnTimeout;
172 
173     /* Directory of databases */
174     protected DbTree dbMapTree;
175     private long mapTreeRootLsn = DbLsn.NULL_LSN;
176     private Latch mapTreeRootLatch;
177 
178     private INList inMemoryINs;
179 
180     /* Services */
181     protected DbConfigManager configManager;
182     private List<EnvConfigObserver> configObservers;
183     protected Logger envLogger;
184     private LogManager logManager;
185     private FileManager fileManager;
186     private TxnManager txnManager;
187     protected StatManager statManager;
188 
189     /* Daemons */
190     private Evictor evictor;
191     private INCompressor inCompressor;
192     private Checkpointer checkpointer;
193     private Cleaner cleaner;
194     private StatCapture statCapture;
195 
196     /* Stats, debug information */
197     protected final StartupTracker startupTracker;
198     private volatile EnvironmentFailureException savedInvalidatingException;
199     private TestHook<Long> cleanerBarrierHoook;
200 
201     /* If true, call Thread.yield() at strategic points (stress test aid) */
202     private static boolean forcedYield = false;
203 
204     /*
205      * Used by Database, SecondaryDatabase and Cursor to protect changes to
206      * secondary associations during operations that use the associations.  A
207      * single latch for all databases is used to prevent deadlocks and to
208      * support associations involving multiple primary databases.
209      *
210      * A ReentrantReadWriteLock is used directly rather than via a SharedLatch.
211      * This is because reentrancy is required but not supported by SharedLatch.
212      */
213     private ReentrantReadWriteLock secondaryAssociationLock;
214 
215     /**
216      * The exception listener for this environment, if any has been specified.
217      */
218     private ExceptionListener exceptionListener = null;
219 
220     /**
221      * The recovery progress listener for this environment, if any has been
222      * specified.
223      */
224     private ProgressListener<RecoveryProgress> recoveryProgressListener = null;
225 
226     /**
227      * ClassLoader used to load user-supplied classes by name.
228      */
229     private ClassLoader classLoader = null;
230 
231     /**
232      * Used for duplicate database conversion.
233      */
234     private PreloadConfig dupConvertPreloadConfig = null;
235 
236     /*
237      * Configuration and tracking of background IO limits.  Managed by the
238      * updateBackgroundReads, updateBackgroundWrites and sleepAfterBackgroundIO
239      * methods.  The limits and the backlog are volatile because we check them
240      * outside the synchronized block.  Other fields are updated and checked
241      * while synchronized on the tracking mutex object.  The sleep mutex is
242      * used to block multiple background threads while sleeping.
243      */
244     private volatile int backgroundSleepBacklog;
245     private volatile int backgroundReadLimit;
246     private volatile int backgroundWriteLimit;
247     private long backgroundSleepInterval;
248     private int backgroundReadCount;
249     private long backgroundWriteBytes;
250     private TestHook<?> backgroundSleepHook;
251     private final Object backgroundTrackingMutex = new Object();
252     private final Object backgroundSleepMutex = new Object();
253 
254     /*
255      * ThreadLocal.get() is not cheap so we want to minimize calls to it.  We
256      * only use ThreadLocals for the TreeStatsAccumulator which are only called
257      * in limited circumstances.  Use this reference count to indicate that a
258      * thread has set a TreeStatsAccumulator.  When it's done, it decrements
259      * the counter.  It's static so that we don't have to pass around the
260      * EnvironmentImpl.
261      */
262     private static int threadLocalReferenceCount = 0;
263 
264     /* Used to prevent multiple full thread dumps. */
265     private boolean didFullThreadDump = false;
266 
267     /**
268      * DbPrintLog doesn't need btree and dup comparators to function properly
269      * don't require any instantiations.  This flag, if true, indicates that
270      * we've been called from DbPrintLog or a similar utility.
271      */
272     private boolean noComparators = false;
273 
274     /*
275      * A preallocated EnvironmentFailureException that is used in OOME and
276      * other java.lang.Error situations so that allocation does not need to be
277      * done in the OOME context.
278      */
279     public final EnvironmentFailureException SAVED_EFE =
280         EnvironmentFailureException.makeJavaErrorWrapper();
281 
282     public static final boolean USE_JAVA5_ADLER32;
283 
284     private static final String DISABLE_JAVA_ADLER32_NAME =
285         "je.disable.java.adler32";
286 
287     static {
288         USE_JAVA5_ADLER32 =
289             System.getProperty(DISABLE_JAVA_ADLER32_NAME) == null;
290     }
291 
292     /*
293      * JE MBeans.
294      *
295      * Note that MBeans are loaded dynamically in order to support platforms
296      * that do not include javax.management.  TODO: Since Dalvik is no longer
297      * supported, we may want to remove this abstraction.
298      */
299 
300     /* The property name of setting these two MBeans. */
301     private static final String REGISTER_MONITOR = "JEMonitor";
302 
303     /* The two MBeans registered or not. */
304     private volatile boolean isMBeanRegistered = false;
305 
306     /*
307      * Log handlers used in java.util.logging. Handlers are per-environment,
308      * and must not be static, because the output is tagged with an identifier
309      * that associates the information with that environment. Handlers should
310      * be closed to release resources when the environment is closed.
311      *
312      * Note that handlers are not statically attached to loggers. See
313      * LoggerUtils.java for information on how redirect loggers are used.
314      */
315     private static final String INFO_FILES = "je.info";
316     private static final int FILEHANDLER_LIMIT = 10000000;
317     private static final int FILEHANDLER_COUNT = 10;
318     private final ConsoleHandler consoleHandler;
319     private final FileHandler fileHandler;
320 
321     /*
322      * A Handler that was specified by the application through
323      * EnvironmentConfig
324      */
325     private final Handler configuredHandler;
326     /* cache this value as a performance optimization. */
327     private boolean dbLoggingDisabled;
328 
329     /* Formatter for java.util.logging. */
330     protected final Formatter formatter;
331 
332     /*
333      * The internal environment handle that is passed to triggers invoked as a
334      * result of AutoTransactions where no environment handle is available, and
335      * in all cases of triggers involving replicated environments.
336      */
337     protected Environment envInternal;
338 
339     /*
340      * Used to coordinate getting stats and shutting down the threads
341      * that provide the stats. The shutdown of the statistics capture
342      * thread will get statistics right before shutting down. The
343      * acquisition of stats must be done without synchronizing on the
344      * EnvironmentImpl to avoid a deadlock between the shutdown thread
345      * (has the EnvironmentImpl lock) and the stat capture thread calling
346      * getStats().
347      */
348     private final Object statSynchronizer = new Object();
349 
350     /* Stat base key used for loadStats api */
351     protected Integer statKey;
352 
353     private long creationTime;
354 
355     /**
356      * To support platforms that do not have any javax.management classes, we
357      * load JEMonitor dynamically to ensure that there are no explicit
358      * references to com.sleepycat.je.jmx.*.
359      */
360     public static interface MBeanRegistrar {
doRegister(Environment env)361         public void doRegister(Environment env)
362             throws Exception;
363 
doUnregister()364         public void doUnregister()
365             throws Exception;
366     }
367 
368     private final ArrayList<MBeanRegistrar> mBeanRegList =
369         new ArrayList<MBeanRegistrar>();
370 
371     /* NodeId sequence counters */
372     private final NodeSequence nodeSequence;
373 
374     /* Stats */
375     private StatGroup stats;
376 
377     private ThroughputStatGroup thrputStats;
378 
379     private CustomStats customStats;
380 
381     /* Number of relatches required in this environment. */
382     private LongStat relatchesRequired;
383 
384     private EnvStatsLogger envStatLogger = null;
385 
386     /* Refer to comment near declaration of these static LockUpgrades. */
387     static {
388         LockUpgrade.ILLEGAL.setUpgrade(null);
389         LockUpgrade.EXISTING.setUpgrade(null);
390         LockUpgrade.WRITE_PROMOTE.setUpgrade(LockType.WRITE);
391         LockUpgrade.RANGE_READ_IMMED.setUpgrade(LockType.RANGE_READ);
392         LockUpgrade.RANGE_WRITE_IMMED.setUpgrade(LockType.RANGE_WRITE);
393         LockUpgrade.RANGE_WRITE_PROMOTE.setUpgrade(LockType.RANGE_WRITE);
394     }
395 
396     /* May be null, see getOptionalNodeName. */
397     private final String optionalNodeName;
398 
399     /* EnvironmentConfig.TREE_COMPACT_MAX_KEY_LENGTH. */
400     private int compactMaxKeyLength;
401 
402     /* EnvironmentParams.ENV_LATCH_TIMEOUT. */
403     private int latchTimeoutMs;
404 
EnvironmentImpl(File envHome, EnvironmentConfig envConfig, EnvironmentImpl sharedCacheEnv)405     public EnvironmentImpl(File envHome,
406                            EnvironmentConfig envConfig,
407                            EnvironmentImpl sharedCacheEnv)
408         throws EnvironmentNotFoundException, EnvironmentLockedException {
409 
410         this(envHome, envConfig, sharedCacheEnv, null);
411     }
412 
413     /**
414      * Create a database environment to represent the data in envHome.
415      * dbHome. Properties from the je.properties file in that directory are
416      * used to initialize the system wide property bag. Properties passed to
417      * this method are used to influence the open itself.
418      *
419      * @param envHome absolute path of the database environment home directory
420      * @param envConfig is the configuration to be used. It's already had
421      *                  the je.properties file applied, and has been validated.
422      * @param sharedCacheEnv if non-null, is another environment that is
423      * sharing the cache with this environment; if null, this environment is
424      * not sharing the cache or is the first environment to share the cache.
425      *
426      * @throws DatabaseException on all other failures
427      *
428      * @throws IllegalArgumentException via Environment ctor.
429      */
EnvironmentImpl(File envHome, EnvironmentConfig envConfig, EnvironmentImpl sharedCacheEnv, RepConfigProxy repConfigProxy)430     protected EnvironmentImpl(File envHome,
431                               EnvironmentConfig envConfig,
432                               EnvironmentImpl sharedCacheEnv,
433                               RepConfigProxy repConfigProxy)
434         throws EnvironmentNotFoundException, EnvironmentLockedException {
435 
436         boolean success = false;
437         startupTracker = new StartupTracker(this);
438         startupTracker.start(Phase.TOTAL_ENV_OPEN);
439 
440         try {
441             this.envHome = envHome;
442             envState = DbEnvState.INIT;
443             mapTreeRootLatch = LatchFactory.createExclusiveLatch(
444                 this, "MapTreeRoot", false /*collectStats*/);
445 
446             /* Do the stats definition. */
447             stats = new StatGroup(ENV_GROUP_NAME, ENV_GROUP_DESC);
448             thrputStats =
449                 new ThroughputStatGroup
450                     (THROUGHPUT_GROUP_NAME, THROUGHPUT_GROUP_DESC);
451 
452             relatchesRequired =
453                 new LongStat(stats, ENVIMPL_RELATCHES_REQUIRED);
454             creationTime = System.currentTimeMillis();
455 
456             /* Set up configuration parameters */
457             configManager = initConfigManager(envConfig, repConfigProxy);
458             configObservers = new ArrayList<EnvConfigObserver>();
459             addConfigObserver(this);
460             initConfigParams(envConfig, repConfigProxy);
461 
462             /*
463              * Create essential services that must exist before recovery.
464              */
465 
466             /*
467              * Set up java.util.logging handlers and their environment specific
468              * formatters. These are used by the redirect handlers, rather
469              * than specific loggers.
470              */
471             formatter = initFormatter();
472             consoleHandler =
473                 new com.sleepycat.je.util.ConsoleHandler(formatter, this);
474             fileHandler = initFileHandler();
475             configuredHandler = envConfig.getLoggingHandler();
476             envLogger = LoggerUtils.getLogger(getClass());
477 
478             /*
479              * Decide on memory budgets based on environment config params and
480              * memory available to this process.
481              */
482             memoryBudget =
483                 new MemoryBudget(this, sharedCacheEnv, configManager);
484 
485             fileManager = new FileManager(this, envHome, isReadOnly);
486             if (!envConfig.getAllowCreate() && !fileManager.filesExist()) {
487                 throw new EnvironmentNotFoundException
488                     (this, "Home directory: " + envHome);
489             }
490 
491             optionalNodeName = envConfig.getNodeName();
492 
493             logManager = new LogManager(this, isReadOnly);
494 
495             inMemoryINs = new INList(this);
496             txnManager = new TxnManager(this);
497             statManager = createStatManager();
498 
499             /*
500              * Daemons are always made here, but only started after recovery.
501              * We want them to exist so we can call them programatically even
502              * if the daemon thread is not started.
503              */
504             createDaemons(sharedCacheEnv);
505 
506             /*
507              * The node sequences are not initialized until after the DbTree is
508              * created below.
509              */
510             nodeSequence = new NodeSequence(this);
511 
512             /*
513              * Instantiate a new, blank dbtree. If the environment already
514              * exists, recovery will recreate the dbMapTree from the log and
515              * overwrite this instance.
516              */
517             dbMapTree = new DbTree(this, isReplicated(), getPreserveVLSN());
518 
519             secondaryAssociationLock =
520                 new ReentrantReadWriteLock(false /*fair*/);
521 
522             /*
523              * Allocate node sequences before recovery. We expressly wait to
524              * allocate it after the DbTree is created, because these sequences
525              * should not be used by the DbTree before recovery has
526              * run. Waiting until now to allocate them will make errors more
527              * evident, since there will be a NullPointerException.
528              */
529             nodeSequence.initRealNodeId();
530 
531             statKey = statManager.registerStatContext();
532             if (!isReadOnly() &&
533                 !isMemOnly() &&
534                 configManager.getBoolean(EnvironmentParams.STATS_COLLECT)) {
535                 envStatLogger = new EnvStatsLogger(this);
536                 addConfigObserver(envStatLogger);
537                 envStatLogger.log();
538             }
539             success = true;
540         } finally {
541             if (!success) {
542                 /* Release any environment locks if there was a problem. */
543                 clearFileManager();
544                 closeHandlers();
545             }
546         }
547     }
548 
549     /**
550      * Create a config manager that holds the configuration properties that
551      * have been passed in. These properties are already validated, and have
552      * had the proper order of precedence applied; that is, the je.properties
553      * file has been applied. The configuration properties need to be available
554      * before the rest of environment creation proceeds.
555      *
556      * This method is overridden by replication environments.
557      *
558      * @param envConfig is the environment configuration to use
559      * @param repParams are the replication configurations to use. In this
560      * case, the Properties bag has been extracted from the configuration
561      * instance, to avoid crossing the compilation firewall.
562      */
initConfigManager(EnvironmentConfig envConfig, RepConfigProxy repParams)563     protected DbConfigManager initConfigManager(EnvironmentConfig envConfig,
564                                                 RepConfigProxy repParams) {
565         return new DbConfigManager(envConfig);
566     }
567 
568     /**
569      * Init configuration params during environment creation.
570      *
571      * This method is overridden by RepImpl to get init params also.  This
572      * allows certain rep params to be accessed from the EnvironmentImpl
573      * constructor using methods such as getPreserveVLSN. The overridden method
574      * calls this method first.
575      * @param repConfigProxy unused
576      */
initConfigParams(EnvironmentConfig envConfig, RepConfigProxy repConfigProxy)577     protected void initConfigParams(EnvironmentConfig envConfig,
578                                     RepConfigProxy repConfigProxy) {
579 
580         forcedYield =
581             configManager.getBoolean(EnvironmentParams.ENV_FORCED_YIELD);
582         isTransactional =
583             configManager.getBoolean(EnvironmentParams.ENV_INIT_TXN);
584         isNoLocking = !(configManager.getBoolean
585                         (EnvironmentParams.ENV_INIT_LOCKING));
586         if (isTransactional && isNoLocking) {
587             if (TEST_NO_LOCKING_MODE) {
588                 isNoLocking = !isTransactional;
589             } else {
590                 throw new IllegalArgumentException
591                     ("Can't set 'je.env.isNoLocking' and " +
592                      "'je.env.isTransactional';");
593             }
594         }
595 
596         isReadOnly = configManager.getBoolean(
597             EnvironmentParams.ENV_RDONLY);
598 
599         isMemOnly = configManager.getBoolean(
600             EnvironmentParams.LOG_MEMORY_ONLY);
601 
602         dbEviction = configManager.getBoolean(
603             EnvironmentParams.ENV_DB_EVICTION);
604 
605         adler32ChunkSize = configManager.getInt(
606             EnvironmentParams.ADLER32_CHUNK_SIZE);
607 
608         sharedCache = configManager.getBoolean(
609             EnvironmentParams.ENV_SHARED_CACHE);
610 
611         dbLoggingDisabled = !configManager.getBoolean(
612             EnvironmentParams.JE_LOGGING_DBLOG);
613 
614         compactMaxKeyLength = configManager.getInt(
615             EnvironmentParams.TREE_COMPACT_MAX_KEY_LENGTH);
616 
617         latchTimeoutMs = configManager.getDuration(
618             EnvironmentParams.ENV_LATCH_TIMEOUT);
619 
620         allowBlindOps = configManager.getBoolean(
621             EnvironmentParams.BIN_DELTA_BLIND_OPS);
622 
623         allowBlindPuts = configManager.getBoolean(
624             EnvironmentParams.BIN_DELTA_BLIND_PUTS);
625 
626         recoveryProgressListener = envConfig.getRecoveryProgressListener();
627         classLoader = envConfig.getClassLoader();
628         dupConvertPreloadConfig = envConfig.getDupConvertPreloadConfig();
629         customStats = envConfig.getCustomStats();
630     }
631 
632     /**
633      * Initialize the environment, including running recovery, if it is not
634      * already initialized.
635      *
636      * Note that this method should be called even when opening additional
637      * handles for an already initialized environment.  If initialization is
638      * still in progress then this method will block until it is finished.
639      *
640      * @return true if we are opening the first handle for this environment and
641      * recovery is run (when ENV_RECOVERY is configured to true); false if we
642      * are opening an additional handle and recovery is not run.
643      */
finishInit(EnvironmentConfig envConfig)644     public synchronized boolean finishInit(EnvironmentConfig envConfig)
645         throws DatabaseException {
646 
647         if (initializedSuccessfully) {
648             return false;
649         }
650 
651         boolean success = false;
652         try {
653 
654             /*
655              * Do not do recovery if this environment is for a utility that
656              * reads the log directly.
657              */
658             final boolean doRecovery =
659                 configManager.getBoolean(EnvironmentParams.ENV_RECOVERY);
660             if (doRecovery) {
661 
662                 /*
663                  * Run recovery.  Note that debug logging to the database log
664                  * is disabled until recovery is finished.
665                  */
666                 boolean recoverySuccess = false;
667                 try {
668                     RecoveryManager recoveryManager =
669                         new RecoveryManager(this);
670                     recoveryManager.recover(isReadOnly);
671 
672                     postRecoveryConversion();
673                     recoverySuccess = true;
674                 } finally {
675                     try {
676 
677                         /*
678                          * Flush to get all exception tracing out to the log.
679                          */
680                         logManager.flush();
681                         fileManager.clear();
682                     } catch (IOException e) {
683                         /* Ignore second order exceptions. */
684                         if (recoverySuccess) {
685                             throw new EnvironmentFailureException
686                                 (this, EnvironmentFailureReason.LOG_INTEGRITY,
687                                  e);
688                         }
689                     } catch (Exception e) {
690                         if (recoverySuccess) {
691                             throw EnvironmentFailureException.
692                                 unexpectedException(this, e);
693                         }
694                     }
695                 }
696             } else {
697                 isReadOnly = true;
698 
699                 /*
700                  * Normally when recovery is skipped, we don't need to
701                  * instantiate comparators.  But even without recovery, some
702                  * utilities such as DbScavenger need comparators.
703                  */
704                 if (!configManager.getBoolean
705                         (EnvironmentParams.ENV_COMPARATORS_REQUIRED)) {
706                     noComparators = true;
707                 }
708             }
709 
710             /*
711              * Cache a few critical values. We keep our timeout in millis
712              * because Object.wait takes millis.
713              */
714             lockTimeout =
715                 configManager.getDuration(EnvironmentParams.LOCK_TIMEOUT);
716             txnTimeout =
717                 configManager.getDuration(EnvironmentParams.TXN_TIMEOUT);
718 
719             /*
720              * Initialize the environment memory usage number. Must be called
721              * after recovery, because recovery determines the starting size of
722              * the in-memory tree.
723              */
724             memoryBudget.initCacheMemoryUsage
725                 (dbMapTree.getTreeAdminMemory());
726 
727             /*
728              * Call config observer and start daemons last after everything
729              * else is initialized. Note that all config parameters, both
730              * mutable and non-mutable, needed by the memoryBudget have already
731              * been initialized when the configManager was instantiated.
732              */
733             envConfigUpdate(configManager, envConfig);
734 
735             /* Mark as open before starting daemons. */
736             open();
737 
738             /*
739              * Mark initialized before creating the internal env, since
740              * otherwise a we'll recurse and attempt to create another
741              * EnvironmentImpl.
742              */
743             initializedSuccessfully = true;
744 
745             if (doRecovery) {
746 
747                 /*
748                  * Perform dup database conversion after recovery and other
749                  * initialization is complete, but before running daemons.
750                  */
751                 convertDupDatabases();
752 
753                 /* Create internal env before SyncCleanerBarrier. */
754                 envInternal = createInternalEnvironment();
755             }
756 
757             runOrPauseDaemons(configManager);
758             success = true;
759             return true;
760         } finally {
761             if (!success) {
762                 /* Release any environment locks if there was a problem. */
763                 clearFileManager();
764                 closeHandlers();
765             }
766 
767             /*
768              * DbEnvPool.addEnvironment is called by RecoveryManager.buildTree
769              * during recovery above, to enable eviction during recovery.  If
770              * we fail to create the environment, we must remove it.
771              */
772             if (!success && sharedCache && evictor != null) {
773                 evictor.removeEnvironment(this);
774             }
775 
776             startupTracker.stop(Phase.TOTAL_ENV_OPEN);
777             startupTracker.setProgress(RecoveryProgress.RECOVERY_FINISHED);
778         }
779     }
780 
781     /**
782      * Is overridden in RepImpl to create a ReplicatedEnvironment.
783      */
createInternalEnvironment()784     protected Environment createInternalEnvironment() {
785         return new InternalEnvironment(getEnvironmentHome(), cloneConfig(),
786                                        this);
787     }
788 
789     /*
790      * JE MBean registration is performed during Environment creation so that
791      * the MBean has access to the Environment API which is not available from
792      * EnvironmentImpl. This precludes registering MBeans in
793      * EnvironmentImpl.finishInit.
794      */
registerMBean(Environment env)795     public synchronized void registerMBean(Environment env)
796         throws DatabaseException {
797 
798         if (!isMBeanRegistered) {
799             if (System.getProperty(REGISTER_MONITOR) != null) {
800                 doRegisterMBean(getMonitorClassName(), env);
801                 doRegisterMBean(getDiagnosticsClassName(), env);
802             }
803             isMBeanRegistered = true;
804         }
805     }
806 
getMonitorClassName()807     protected String getMonitorClassName() {
808         return "com.sleepycat.je.jmx.JEMonitor";
809     }
810 
getDiagnosticsClassName()811     protected String getDiagnosticsClassName() {
812         return "com.sleepycat.je.jmx.JEDiagnostics";
813     }
814 
815     /*
816      * Returns the default consistency policy for this EnvironmentImpl.
817      *
818      * When a Txn is created directly for internal use, the default consistency
819      * is needed.  For example, SyncDB uses this method.
820      *
821      * This method returns null for a standalone Environment, and returns the
822      * default consistency policy for a ReplicatedEnvironment.
823      */
getDefaultConsistencyPolicy()824     public ReplicaConsistencyPolicy getDefaultConsistencyPolicy() {
825         return null;
826     }
827 
828     /* Return the durable VLSN for the replication group. */
getGroupDurableVLSN()829     public VLSN getGroupDurableVLSN() {
830         throw new UnsupportedOperationException
831             ("Standalone Environment doesn't support returning " +
832              "GlobalDurableVLSN.");
833     }
834 
835     /* Returns the on disk LSN for a VLSN. */
getLsnForVLSN(@uppressWarningsR) VLSN vlsn, @SuppressWarnings(R) int readBufferSize)836     public long getLsnForVLSN(@SuppressWarnings("unused") VLSN vlsn,
837                               @SuppressWarnings("unused") int readBufferSize) {
838         throw new UnsupportedOperationException
839             ("Standalone Environment doesn't support finding LSN for VLSN.");
840     }
841 
842     /* Disable the LocalCBVLSN changes on a replicator. */
freezeLocalCBVLSN()843     public void freezeLocalCBVLSN() {
844         throw new UnsupportedOperationException
845             ("Standalone Environment doesn't support LocalCBVLSN.");
846     }
847 
848     /* Enable the LocalCBVLSN changes on a replicator. */
unfreezeLocalCBVLSN()849     public void unfreezeLocalCBVLSN() {
850         throw new UnsupportedOperationException
851             ("Standalone Environment doesn't support LocalCBVLSN.");
852     }
853 
854     /*
855      * Returns the end of the log.
856      *
857      * Returned value is a Lsn if it's a standalone Environment, otherwise it's
858      * a VLSN.
859      */
getEndOfLog()860     public long getEndOfLog() {
861         return fileManager.getLastUsedLsn();
862     }
863 
864     /* Get replication statistics. */
getRepStatGroups(StatsConfig config, Integer statkey)865     public Collection<StatGroup> getRepStatGroups(StatsConfig config,
866                                                   Integer statkey) {
867         throw new UnsupportedOperationException
868             ("Standalone Environment doesn't support replication statistics.");
869     }
870 
getStatCaptureProjections()871     public SortedSet<String> getStatCaptureProjections() {
872         return new StatCaptureDefinitions().getStatisticProjections();
873     }
874 
createStatManager()875     public StatManager createStatManager() {
876         return new StatManager(this);
877     }
878 
doRegisterMBean(String className, Environment env)879     private void doRegisterMBean(String className, Environment env)
880         throws DatabaseException {
881 
882         try {
883             Class<?> newClass = Class.forName(className);
884             MBeanRegistrar mBeanReg = (MBeanRegistrar) newClass.newInstance();
885             mBeanReg.doRegister(env);
886             mBeanRegList.add(mBeanReg);
887         } catch (Exception e) {
888             throw new EnvironmentFailureException
889                 (DbInternal.getEnvironmentImpl(env),
890                  EnvironmentFailureReason.MONITOR_REGISTRATION, e);
891         }
892     }
893 
unregisterMBean()894     private synchronized void unregisterMBean()
895         throws Exception {
896 
897         for (MBeanRegistrar mBeanReg : mBeanRegList) {
898             mBeanReg.doUnregister();
899         }
900     }
901 
902     /*
903      * Release and close the FileManager when there are problems during the
904      * initialization of this EnvironmentImpl.  An exception is already in
905      * flight when this method is called.
906      */
clearFileManager()907     private void clearFileManager()
908         throws DatabaseException {
909 
910         if (fileManager != null) {
911             try {
912 
913                 /*
914                  * Clear again, in case an exception in logManager.flush()
915                  * caused us to skip the earlier call to clear().
916                  */
917                 fileManager.clear();
918             } catch (Throwable e) {
919 
920                 /*
921                  * Klockwork - ok
922                  * Eat it, we want to throw the original exception.
923                  */
924             }
925             try {
926                 fileManager.close();
927             } catch (Throwable e) {
928 
929                 /*
930                  * Klockwork - ok
931                  * Eat it, we want to throw the original exception.
932                  */
933             }
934         }
935     }
936 
937     /**
938      * Respond to config updates.
939      */
940     @Override
envConfigUpdate(DbConfigManager mgr, EnvironmentMutableConfig newConfig)941     public void envConfigUpdate(DbConfigManager mgr,
942                                 EnvironmentMutableConfig newConfig) {
943         backgroundReadLimit = mgr.getInt
944             (EnvironmentParams.ENV_BACKGROUND_READ_LIMIT);
945         backgroundWriteLimit = mgr.getInt
946             (EnvironmentParams.ENV_BACKGROUND_WRITE_LIMIT);
947         backgroundSleepInterval = mgr.getDuration
948             (EnvironmentParams.ENV_BACKGROUND_SLEEP_INTERVAL);
949 
950         /* Reset logging levels if they're set in EnvironmentMutableConfig. */
951         if (newConfig.isConfigParamSet
952                 (EnvironmentConfig.CONSOLE_LOGGING_LEVEL)) {
953             Level newConsoleHandlerLevel =
954                 Level.parse(mgr.get(EnvironmentParams.JE_CONSOLE_LEVEL));
955             consoleHandler.setLevel(newConsoleHandlerLevel);
956         }
957 
958         if (newConfig.isConfigParamSet
959                 (EnvironmentConfig.FILE_LOGGING_LEVEL)) {
960             Level newFileHandlerLevel =
961                 Level.parse(mgr.get(EnvironmentParams.JE_FILE_LEVEL));
962             if (fileHandler != null) {
963                 fileHandler.setLevel(newFileHandlerLevel);
964             }
965         }
966 
967         exceptionListener = newConfig.getExceptionListener();
968 
969         cacheMode = newConfig.getCacheMode();
970         cacheModeStrategy = newConfig.getCacheModeStrategy();
971 
972         if (mgr.getBoolean(EnvironmentParams.STATS_COLLECT)) {
973             if (envStatLogger == null &&
974                 !isReadOnly() &&
975                 !isMemOnly() ) {
976                 envStatLogger = new EnvStatsLogger(this);
977                 addConfigObserver(envStatLogger);
978 
979                 /*
980                  * Need to log env stats because stats were off and are now on.
981                  * Since stats were off there was no event observer registered.
982                  */
983                 envStatLogger.log();
984             }
985 
986             /* Create stat capture object if needed */
987             createStatCapture();
988 
989         } else {
990             if (envStatLogger != null) {
991                 removeConfigObserver(envStatLogger);
992             }
993             envStatLogger = null;
994             shutdownStatCapture();
995         }
996 
997         /*
998          * Start daemons last, after all other parameters are set.  Do not
999          * start the daemons during the EnvironmentImpl constructor's call
1000          * (before open() has been called), because this must be done after
1001          * creating the SyncCleanerBarrier.
1002          */
1003         if (isValid()) {
1004             runOrPauseDaemons(mgr);
1005         }
1006     }
1007 
1008     /**
1009      * Read configurations for daemons, instantiate.
1010      */
createDaemons(EnvironmentImpl sharedCacheEnv)1011     private void createDaemons(EnvironmentImpl sharedCacheEnv)
1012         throws DatabaseException {
1013 
1014         /* Evictor */
1015         if (sharedCacheEnv != null) {
1016             assert sharedCache;
1017             evictor = sharedCacheEnv.evictor;
1018         } else {
1019             evictor = new Evictor(this);
1020         }
1021 
1022         /* Checkpointer */
1023 
1024         /*
1025          * Make sure that either log-size-based or time-based checkpointing
1026          * is enabled.
1027          */
1028         long checkpointerWakeupTime =
1029             Checkpointer.getWakeupPeriod(configManager);
1030         checkpointer = new Checkpointer(this,
1031                                         checkpointerWakeupTime,
1032                                         Environment.CHECKPOINTER_NAME);
1033 
1034         /* INCompressor */
1035         long compressorWakeupInterval = configManager.getDuration
1036             (EnvironmentParams.COMPRESSOR_WAKEUP_INTERVAL);
1037         inCompressor = new INCompressor(this, compressorWakeupInterval,
1038                                         Environment.INCOMP_NAME);
1039 
1040         /* The cleaner is not time-based so no wakeup interval is used. */
1041         cleaner = new Cleaner(this, Environment.CLEANER_NAME);
1042 
1043         createStatCapture();
1044     }
1045 
1046     /**
1047      * Run or pause daemons, depending on config properties.
1048      */
runOrPauseDaemons(DbConfigManager mgr)1049     private void runOrPauseDaemons(DbConfigManager mgr) {
1050         if (!isReadOnly) {
1051             /* INCompressor */
1052             inCompressor.runOrPause
1053                 (mgr.getBoolean(EnvironmentParams.ENV_RUN_INCOMPRESSOR));
1054 
1055             /* Cleaner. Do not start it if running in-memory  */
1056             cleaner.runOrPause
1057                 (mgr.getBoolean(EnvironmentParams.ENV_RUN_CLEANER) &&
1058                  !isMemOnly);
1059 
1060             /*
1061              * Checkpointer. Run in both transactional and non-transactional
1062              * environments to guarantee recovery time.
1063              */
1064             checkpointer.runOrPause
1065                 (mgr.getBoolean(EnvironmentParams.ENV_RUN_CHECKPOINTER));
1066 
1067             /* Stat capture */
1068             if (statCapture != null) {
1069                 statCapture.runOrPause
1070                     (mgr.getBoolean(EnvironmentParams.STATS_COLLECT));
1071             }
1072         }
1073     }
1074 
1075     /**
1076      * Return the incompressor. In general, don't use this directly because
1077      * it's easy to forget that the incompressor can be null at times (i.e
1078      * during the shutdown procedure. Instead, wrap the functionality within
1079      * this class, like lazyCompress.
1080      */
getINCompressor()1081     public INCompressor getINCompressor() {
1082         return inCompressor;
1083     }
1084 
1085     /**
1086      * Returns the UtilizationTracker.
1087      */
getUtilizationTracker()1088     public UtilizationTracker getUtilizationTracker() {
1089         return cleaner.getUtilizationTracker();
1090     }
1091 
1092     /**
1093      * Returns the UtilizationProfile.
1094      */
getUtilizationProfile()1095     public UtilizationProfile getUtilizationProfile() {
1096         return cleaner.getUtilizationProfile();
1097     }
1098 
1099     /**
1100      * Returns the default cache mode for this environment. If the environment
1101      * has a null cache mode, CacheMode.DEFAULT is returned.  Null is never
1102      * returned.
1103      */
getDefaultCacheMode()1104     public CacheMode getDefaultCacheMode() {
1105         if (cacheMode != null) {
1106             return cacheMode;
1107         }
1108         return CacheMode.DEFAULT;
1109     }
1110 
1111     /**
1112      * Returns the environment cache mode strategy.  Null may be returned.
1113      */
getDefaultCacheModeStrategy()1114     public CacheModeStrategy getDefaultCacheModeStrategy() {
1115         return cacheModeStrategy;
1116     }
1117 
1118     /**
1119      * Returns EnvironmentConfig.TREE_COMPACT_MAX_KEY_LENGTH.
1120      */
getCompactMaxKeyLength()1121     public int getCompactMaxKeyLength() {
1122         return compactMaxKeyLength;
1123     }
1124 
1125     /**
1126      * Returns EnvironmentParams.ENV_LATCH_TIMEOUT.
1127      */
getLatchTimeoutMs()1128     public int getLatchTimeoutMs() {
1129         return latchTimeoutMs;
1130     }
1131 
1132     /**
1133      * If a background read limit has been configured and that limit is
1134      * exceeded when the cumulative total is incremented by the given number of
1135      * reads, increment the sleep backlog to cause a sleep to occur.  Called by
1136      * background activities such as the cleaner after performing a file read
1137      * operation.
1138      *
1139      * @see #sleepAfterBackgroundIO
1140      */
updateBackgroundReads(int nReads)1141     public void updateBackgroundReads(int nReads) {
1142 
1143         /*
1144          * Make a copy of the volatile limit field since it could change
1145          * between the time we check it and the time we use it below.
1146          */
1147         int limit = backgroundReadLimit;
1148         if (limit > 0) {
1149             synchronized (backgroundTrackingMutex) {
1150                 backgroundReadCount += nReads;
1151                 if (backgroundReadCount >= limit) {
1152                     backgroundSleepBacklog += 1;
1153                     /* Remainder is rolled forward. */
1154                     backgroundReadCount -= limit;
1155                     assert backgroundReadCount >= 0;
1156                 }
1157             }
1158         }
1159     }
1160 
1161     /**
1162      * If a background write limit has been configured and that limit is
1163      * exceeded when the given amount written is added to the cumulative total,
1164      * increment the sleep backlog to cause a sleep to occur.  Called by
1165      * background activities such as the checkpointer and evictor after
1166      * performing a file write operation.
1167      *
1168      * <p>The number of writes is estimated by dividing the bytes written by
1169      * the log buffer size.  Since the log write buffer is shared by all
1170      * writers, this is the best approximation possible.</p>
1171      *
1172      * @see #sleepAfterBackgroundIO
1173      */
updateBackgroundWrites(int writeSize, int logBufferSize)1174     public void updateBackgroundWrites(int writeSize, int logBufferSize) {
1175 
1176         /*
1177          * Make a copy of the volatile limit field since it could change
1178          * between the time we check it and the time we use it below.
1179          */
1180         int limit = backgroundWriteLimit;
1181         if (limit > 0) {
1182             synchronized (backgroundTrackingMutex) {
1183                 backgroundWriteBytes += writeSize;
1184                 int writeCount = (int) (backgroundWriteBytes / logBufferSize);
1185                 if (writeCount >= limit) {
1186                     backgroundSleepBacklog += 1;
1187                     /* Remainder is rolled forward. */
1188                     backgroundWriteBytes -= (limit * logBufferSize);
1189                     assert backgroundWriteBytes >= 0;
1190                 }
1191             }
1192         }
1193     }
1194 
1195     /**
1196      * If the sleep backlog is non-zero (set by updateBackgroundReads or
1197      * updateBackgroundWrites), sleep for the configured interval and decrement
1198      * the backlog.
1199      *
1200      * <p>If two threads call this method and the first call causes a sleep,
1201      * the call by the second thread will block until the first thread's sleep
1202      * interval is over.  When the call by the second thread is unblocked, if
1203      * another sleep is needed then the second thread will sleep again.  In
1204      * other words, when lots of sleeps are needed, background threads may
1205      * backup.  This is intended to give foreground threads a chance to "catch
1206      * up" when background threads are doing a lot of IO.</p>
1207      */
sleepAfterBackgroundIO()1208     public void sleepAfterBackgroundIO() {
1209         if (backgroundSleepBacklog > 0) {
1210             synchronized (backgroundSleepMutex) {
1211                 /* Sleep. Rethrow interrupts if they occur. */
1212                 try {
1213                     /* FindBugs: OK that we're sleeping with a mutex held. */
1214                     Thread.sleep(backgroundSleepInterval);
1215                 } catch (InterruptedException e) {
1216                     Thread.currentThread().interrupt();
1217                 }
1218                 /* Assert has intentional side effect for unit testing. */
1219                 assert TestHookExecute.doHookIfSet(backgroundSleepHook);
1220             }
1221             synchronized (backgroundTrackingMutex) {
1222                 /* Decrement backlog last to make other threads wait. */
1223                 if (backgroundSleepBacklog > 0) {
1224                     backgroundSleepBacklog -= 1;
1225                 }
1226             }
1227         }
1228     }
1229 
1230     /* For unit testing only. */
setBackgroundSleepHook(TestHook<?> hook)1231     public void setBackgroundSleepHook(TestHook<?> hook) {
1232         backgroundSleepHook = hook;
1233     }
1234 
1235     /* For unit testing only. */
setCleanerBarrierHook(TestHook<Long> hook)1236     public void setCleanerBarrierHook(TestHook<Long> hook) {
1237         cleanerBarrierHoook = hook;
1238     }
1239 
1240     /**
1241      * Logs the map tree root and saves the LSN.
1242      */
logMapTreeRoot()1243     public void logMapTreeRoot()
1244         throws DatabaseException {
1245 
1246         logMapTreeRoot(DbLsn.NULL_LSN);
1247     }
1248 
1249     /**
1250      * Logs the map tree root, but only if its current LSN is before the
1251      * ifBeforeLsn parameter or ifBeforeLsn is NULL_LSN.
1252      */
logMapTreeRoot(long ifBeforeLsn)1253     public void logMapTreeRoot(long ifBeforeLsn)
1254         throws DatabaseException {
1255 
1256         mapTreeRootLatch.acquireExclusive();
1257         try {
1258             if (ifBeforeLsn == DbLsn.NULL_LSN ||
1259                 DbLsn.compareTo(mapTreeRootLsn, ifBeforeLsn) < 0) {
1260                 mapTreeRootLsn = logManager.log(
1261                     SingleItemEntry.create(LogEntryType.LOG_DBTREE,
1262                                            dbMapTree),
1263                      ReplicationContext.NO_REPLICATE);
1264             }
1265         } finally {
1266             mapTreeRootLatch.release();
1267         }
1268     }
1269 
1270     /**
1271      * Force a rewrite of the map tree root if required.
1272      */
rewriteMapTreeRoot(long cleanerTargetLsn)1273     public void rewriteMapTreeRoot(long cleanerTargetLsn)
1274         throws DatabaseException {
1275 
1276         mapTreeRootLatch.acquireExclusive();
1277         try {
1278             if (DbLsn.compareTo(cleanerTargetLsn, mapTreeRootLsn) == 0) {
1279 
1280                 /*
1281                  * The root entry targetted for cleaning is in use.  Write a
1282                  * new copy.
1283                  */
1284                 mapTreeRootLsn = logManager.log(
1285                     SingleItemEntry.create(LogEntryType.LOG_DBTREE,
1286                                            dbMapTree),
1287                      ReplicationContext.NO_REPLICATE);
1288             }
1289         } finally {
1290             mapTreeRootLatch.release();
1291         }
1292     }
1293 
1294     /**
1295      * @return the mapping tree root LSN.
1296      */
getRootLsn()1297     public long getRootLsn() {
1298         return mapTreeRootLsn;
1299     }
1300 
1301     /**
1302      * Set the mapping tree from the log. Called during recovery.
1303      */
readMapTreeFromLog(long rootLsn)1304     public void readMapTreeFromLog(long rootLsn)
1305         throws DatabaseException {
1306 
1307         if (dbMapTree != null) {
1308             dbMapTree.close();
1309         }
1310         dbMapTree = (DbTree) logManager.getEntryHandleFileNotFound(rootLsn);
1311 
1312         /* Set the dbMapTree to replicated when converted. */
1313         if (!dbMapTree.isReplicated() && getAllowRepConvert()) {
1314             dbMapTree.setIsReplicated();
1315             dbMapTree.setIsRepConverted();
1316             needRepConvert = true;
1317         }
1318 
1319         dbMapTree.initExistingEnvironment(this);
1320 
1321         /* Set the map tree root */
1322         mapTreeRootLatch.acquireExclusive();
1323         try {
1324             mapTreeRootLsn = rootLsn;
1325         } finally {
1326             mapTreeRootLatch.release();
1327         }
1328     }
1329 
1330     /**
1331      * Tells the asynchronous IN compressor thread about a BIN with a deleted
1332      * entry.
1333      */
addToCompressorQueue(BIN bin, boolean doWakeup)1334     public void addToCompressorQueue(BIN bin, boolean doWakeup) {
1335 
1336         /*
1337          * May be called by the cleaner on its last cycle, after the compressor
1338          * is shut down.
1339          */
1340         if (inCompressor != null) {
1341             inCompressor.addBinToQueue(bin, doWakeup);
1342         }
1343     }
1344 
1345     /**
1346      * Tells the asynchronous IN compressor thread about a BINReference with a
1347      * deleted entry.
1348      */
addToCompressorQueue(BINReference binRef, boolean doWakeup)1349     public void addToCompressorQueue(BINReference binRef, boolean doWakeup) {
1350 
1351         /*
1352          * May be called by the cleaner on its last cycle, after the compressor
1353          * is shut down.
1354          */
1355         if (inCompressor != null) {
1356             inCompressor.addBinRefToQueue(binRef, doWakeup);
1357         }
1358     }
1359 
1360     /**
1361      * Tells the asynchronous IN compressor thread about a collections of
1362      * BINReferences with deleted entries.
1363      */
addToCompressorQueue(Collection<BINReference> binRefs, boolean doWakeup)1364     public void addToCompressorQueue(Collection<BINReference> binRefs,
1365                                      boolean doWakeup) {
1366 
1367         /*
1368          * May be called by the cleaner on its last cycle, after the compressor
1369          * is shut down.
1370          */
1371         if (inCompressor != null) {
1372             inCompressor.addMultipleBinRefsToQueue(binRefs, doWakeup);
1373         }
1374     }
1375 
1376     /**
1377      * Do lazy compression at opportune moments.
1378      */
lazyCompress(IN in)1379     public void lazyCompress(IN in)
1380         throws DatabaseException {
1381 
1382         /*
1383          * May be called by the cleaner on its last cycle, after the compressor
1384          * is shut down.
1385          */
1386         if (inCompressor != null) {
1387             inCompressor.lazyCompress(in);
1388         }
1389     }
1390 
1391     /**
1392      * Reset the logging level for specified loggers in a JE environment.
1393      *
1394      * @throws IllegalArgumentException via JEDiagnostics.OP_RESET_LOGGING
1395      */
resetLoggingLevel(String changedLoggerName, Level level)1396     public void resetLoggingLevel(String changedLoggerName, Level level) {
1397 
1398         /*
1399          * Go through the loggers registered in the global log manager, and
1400          * set the new level. If the specified logger name is not valid, throw
1401          * an IllegalArgumentException.
1402          */
1403         java.util.logging.LogManager loggerManager =
1404             java.util.logging.LogManager.getLogManager();
1405         Enumeration<String> loggers = loggerManager.getLoggerNames();
1406         boolean validName = false;
1407 
1408         while (loggers.hasMoreElements()) {
1409             String loggerName = loggers.nextElement();
1410             Logger logger = loggerManager.getLogger(loggerName);
1411 
1412             if ("all".equals(changedLoggerName) ||
1413                 loggerName.endsWith(changedLoggerName) ||
1414                 loggerName.endsWith(changedLoggerName +
1415                                     LoggerUtils.NO_ENV) ||
1416                 loggerName.endsWith(changedLoggerName +
1417                                     LoggerUtils.FIXED_PREFIX) ||
1418                 loggerName.startsWith(changedLoggerName)) {
1419 
1420                 logger.setLevel(level);
1421                 validName = true;
1422             }
1423         }
1424 
1425         if (!validName) {
1426             throw new IllegalArgumentException
1427                 ("The logger name parameter: " + changedLoggerName +
1428                  " is invalid!");
1429         }
1430     }
1431 
1432     /* Initialize the handler's formatter. */
initFormatter()1433     protected Formatter initFormatter() {
1434         return new TracerFormatter(getName());
1435     }
1436 
initFileHandler()1437     private FileHandler initFileHandler()
1438         throws DatabaseException {
1439 
1440         /*
1441          * Note that in JE 3.X and earlier, file logging encompassed both
1442          * logging to a java.util.logging.FileHandler and our own JE log files
1443          * and logging was disabled for read only and in-memory environments.
1444          * Now that these two concepts are separated, file logging is supported
1445          * for in-memory environments. File logging can be supported as long as
1446          * there is a valid environment home.
1447          */
1448         if ((envHome == null) || (!envHome.isDirectory()) || isReadOnly) {
1449 
1450             /*
1451              * Return null if no environment home directory(therefore no place
1452              * to put file handler output files), or if the Environment is read
1453              * only.
1454              */
1455             return null;
1456         }
1457 
1458         String handlerName = com.sleepycat.je.util.FileHandler.class.getName();
1459         String logFilePattern = envHome + "/" + INFO_FILES;
1460 
1461         /* Log with a rotating set of files, use append mode. */
1462         int limit = FILEHANDLER_LIMIT;
1463         String logLimit =
1464             LoggerUtils.getLoggerProperty(handlerName + ".limit");
1465         if (logLimit != null) {
1466             limit = Integer.parseInt(logLimit);
1467         }
1468 
1469         /* Limit the number of files. */
1470         int count = FILEHANDLER_COUNT;
1471         String logCount =
1472             LoggerUtils.getLoggerProperty(handlerName + ".count");
1473         if (logCount != null) {
1474             count = Integer.parseInt(logCount);
1475         }
1476 
1477         try {
1478             return new com.sleepycat.je.util.FileHandler(logFilePattern,
1479                                                          limit,
1480                                                          count,
1481                                                          formatter,
1482                                                          this);
1483         } catch (IOException e) {
1484             throw EnvironmentFailureException.unexpectedException
1485                 ("Problem creating output files in: " + logFilePattern, e);
1486         }
1487     }
1488 
getConsoleHandler()1489     public ConsoleHandler getConsoleHandler() {
1490         return consoleHandler;
1491     }
1492 
getFileHandler()1493     public FileHandler getFileHandler() {
1494         return fileHandler;
1495     }
1496 
getConfiguredHandler()1497     public Handler getConfiguredHandler() {
1498         return configuredHandler;
1499     }
1500 
closeHandlers()1501     public void closeHandlers() {
1502         if (consoleHandler != null) {
1503             consoleHandler.close();
1504         }
1505 
1506         if (fileHandler != null) {
1507             fileHandler.close();
1508         }
1509     }
1510 
1511     /**
1512      * Not much to do, mark state.
1513      */
open()1514     public void open() {
1515         envState = DbEnvState.OPEN;
1516     }
1517 
1518     /**
1519      * Invalidate the environment. Done when a fatal exception
1520      * (EnvironmentFailureException) is thrown.
1521      */
invalidate(EnvironmentFailureException e)1522     public void invalidate(EnvironmentFailureException e) {
1523 
1524         /*
1525          * Remember the fatal exception so we can redisplay it if the
1526          * environment is called by the application again.
1527          */
1528         savedInvalidatingException = e;
1529         envState = DbEnvState.INVALID;
1530         requestShutdownDaemons();
1531     }
1532 
getInvalidatingException()1533     public EnvironmentFailureException getInvalidatingException() {
1534         return savedInvalidatingException;
1535     }
1536 
1537     /**
1538      * Invalidate the environment when a Java Error is thrown.
1539      */
invalidate(Error e)1540     public void invalidate(Error e) {
1541         if (SAVED_EFE.getCause() != null) {
1542             /* Cause was previously set. */
1543             return;
1544         }
1545         try {
1546             SAVED_EFE.initCause(e);
1547         } catch (IllegalStateException ignored) {
1548             /* Cause was set by another concurrent thread. [#22837] */
1549             return;
1550         }
1551         invalidate(SAVED_EFE);
1552     }
1553 
1554     /**
1555      * Predicate used to determine whether the EnvironmentImpl is currently
1556      * invalid. Note that INVALID is not a terminal state and it could
1557      * subsequently transition to the terminal CLOSED state.
1558      *
1559      * @return true if the environment is invalid
1560      */
isInvalid()1561     public boolean isInvalid() {
1562         return (envState == DbEnvState.INVALID);
1563     }
1564 
1565     /**
1566      * Returns true if the environment is currently invalid or was invalidated
1567      * and closed.
1568      */
wasInvalidated()1569     public boolean wasInvalidated() {
1570         return (envState == DbEnvState.INVALID) ||
1571                ((envState == DbEnvState.CLOSED) &&
1572                 (savedInvalidatingException != null));
1573     }
1574 
1575     /**
1576      * @return true if environment is open.
1577      */
isValid()1578     public boolean isValid() {
1579         return (envState == DbEnvState.OPEN);
1580     }
1581 
1582     /**
1583      * @return true if environment is still in init
1584      */
isInInit()1585     public boolean isInInit() {
1586         return (envState == DbEnvState.INIT);
1587     }
1588 
1589     /**
1590      * @return true if close has begun, although the state may still be open.
1591      */
isClosing()1592     public boolean isClosing() {
1593         return closing;
1594     }
1595 
isClosed()1596     public boolean isClosed() {
1597         return (envState == DbEnvState.CLOSED);
1598     }
1599 
1600     /**
1601      * When a EnvironmentFailureException occurs or the environment is closed,
1602      * further writing can cause log corruption.
1603      */
mayNotWrite()1604     public boolean mayNotWrite() {
1605         return (envState == DbEnvState.INVALID) ||
1606                (envState == DbEnvState.CLOSED);
1607     }
1608 
checkIfInvalid()1609     public void checkIfInvalid()
1610         throws EnvironmentFailureException {
1611 
1612         if (envState == DbEnvState.INVALID) {
1613 
1614             /*
1615              * Set a flag in the exception so the exception message will be
1616              * clear that this was an earlier exception.
1617              */
1618             savedInvalidatingException.setAlreadyThrown(true);
1619 
1620             if (savedInvalidatingException == SAVED_EFE) {
1621                 savedInvalidatingException.fillInStackTrace();
1622                 /* Do not wrap to avoid allocations after an OOME. */
1623                 throw savedInvalidatingException;
1624             }
1625 
1626             throw savedInvalidatingException.wrapSelf
1627                 ("Environment must be closed, caused by: " +
1628                  savedInvalidatingException);
1629         }
1630     }
1631 
checkNotClosed()1632     public void checkNotClosed()
1633         throws DatabaseException {
1634 
1635         if (envState == DbEnvState.CLOSED) {
1636             throw new IllegalStateException
1637                 ("Attempt to use a Environment that has been closed.");
1638         }
1639     }
1640 
1641     /**
1642      * Decrements the reference count and closes the environment when it
1643      * reaches zero.  A checkpoint is always performed when closing.
1644      */
close()1645     public void close()
1646         throws DatabaseException {
1647 
1648         /* Calls doClose while synchronized on DbEnvPool. */
1649         DbEnvPool.getInstance().closeEnvironment
1650             (this, true /*doCheckpoint*/, false /*isAbnormalClose*/);
1651     }
1652 
1653     /**
1654      * Decrements the reference count and closes the environment when it
1655      * reaches zero.  A checkpoint when closing is optional.
1656      */
close(boolean doCheckpoint)1657     public void close(boolean doCheckpoint)
1658         throws DatabaseException {
1659 
1660         /* Calls doClose while synchronized on DbEnvPool. */
1661         DbEnvPool.getInstance().closeEnvironment
1662             (this, doCheckpoint, false /*isAbnormalClose*/);
1663     }
1664 
1665     /**
1666      * Used by error handling to forcibly close an environment, and by tests to
1667      * close an environment to simulate a crash.  Database handles do not have
1668      * to be closed before calling this method.  A checkpoint is not performed.
1669      */
abnormalClose()1670     public void abnormalClose()
1671         throws DatabaseException {
1672 
1673         /* Discard the internal handle, for an abnormal close. */
1674         closeInternalEnvHandle(true);
1675 
1676         /*
1677          * We are assuming that the environment will be cleared out of the
1678          * environment pool, so it's safe to assert that the reference and open
1679          * counts are zero.
1680          */
1681         int openCount1 = getOpenCount();
1682         if (openCount1 > 1) {
1683             throw EnvironmentFailureException.unexpectedState
1684                 (this, "Abnormal close assumes that the open count on " +
1685                  "this handle is 1, not " + openCount1);
1686         }
1687 
1688         int backupCount1 = getBackupCount();
1689         if (backupCount1 > 0) {
1690             throw EnvironmentFailureException.unexpectedState
1691                 (this, "Abnormal close assumes that the backup count on " +
1692                  "this handle is 0, not " + backupCount1);
1693         }
1694 
1695         /* Calls doClose while synchronized on DbEnvPool. */
1696         DbEnvPool.getInstance().closeEnvironment
1697             (this, false /*doCheckpoint*/, true /*isAbnormalClose*/);
1698     }
1699 
1700     /**
1701      * Closes the environment, optionally performing a checkpoint and checking
1702      * for resource leaks.  This method must be called while synchronized on
1703      * DbEnvPool.
1704      *
1705      * @throws IllegalStateException if the environment is already closed.
1706      *
1707      * @throws EnvironmentFailureException if leaks or other problems are
1708      * detected while closing.
1709      */
doClose(boolean doCheckpoint, boolean isAbnormalClose)1710     synchronized void doClose(boolean doCheckpoint, boolean isAbnormalClose) {
1711 
1712         /* Discard the internal handle. */
1713         closeInternalEnvHandle(isAbnormalClose);
1714 
1715         StringWriter errorStringWriter = new StringWriter();
1716         PrintWriter errors = new PrintWriter(errorStringWriter);
1717 
1718         try {
1719             Trace.traceLazily
1720                 (this, "Close of environment " + envHome + " started");
1721             LoggerUtils.fine(envLogger,
1722                              this,
1723                              "Close of environment " + envHome + " started");
1724 
1725             envState.checkState(DbEnvState.VALID_FOR_CLOSE,
1726                                 DbEnvState.CLOSED);
1727 
1728             try {
1729                 setupClose(errors);
1730             } catch (Exception e) {
1731                 appendException(errors, e, "releasing resources");
1732             }
1733 
1734             /*
1735              * If backups are in progress, warn the caller that it was a
1736              * mistake to close the environment at this time.
1737              */
1738             if (getBackupCount() > 0) {
1739                 errors.append("\nThere are backups in progress so the ");
1740                 errors.append("Environment should not have been closed.");
1741                 errors.println();
1742             }
1743 
1744             /*
1745              * Begin shutdown of the deamons before checkpointing.  Cleaning
1746              * during the checkpoint is wasted and slows down the checkpoint.
1747              */
1748             requestShutdownDaemons();
1749 
1750             try {
1751                 unregisterMBean();
1752             } catch (Exception e) {
1753                 appendException(errors, e, "unregistering MBean");
1754             }
1755 
1756             /* Checkpoint to bound recovery time. */
1757             boolean checkpointHappened = false;
1758             if (doCheckpoint &&
1759                 !isReadOnly &&
1760                 (envState != DbEnvState.INVALID) &&
1761                 logManager.getLastLsnAtRecovery() !=
1762                 fileManager.getLastUsedLsn()) {
1763 
1764                 /*
1765                  * Force a checkpoint. Don't allow deltas (minimize recovery
1766                  * time) because they cause inefficiencies for two reasons: (1)
1767                  * recovering BIN-deltas causes extra random I/O in order to
1768                  * reconstitute BINS, which can greatly increase recovery time,
1769                  * and (2) logging deltas during close causes redundant logging
1770                  * by the full checkpoint after recovery.
1771                  */
1772                 CheckpointConfig ckptConfig = new CheckpointConfig();
1773                 ckptConfig.setForce(true);
1774                 ckptConfig.setMinimizeRecoveryTime(true);
1775                 try {
1776                     invokeCheckpoint(ckptConfig, "close");
1777                 } catch (DatabaseException e) {
1778                     appendException(errors, e, "performing checkpoint");
1779                 }
1780                 checkpointHappened = true;
1781             }
1782 
1783             try {
1784                 postCheckpointClose(checkpointHappened);
1785             } catch (Exception e) {
1786                 appendException(errors, e, "after checkpoint");
1787             }
1788 
1789             LoggerUtils.fine(envLogger,
1790                              this,
1791                              "About to shutdown daemons for Env " + envHome);
1792             shutdownDaemons();
1793 
1794             /* Flush log. */
1795             if (!isAbnormalClose) {
1796                 try {
1797                     logManager.flush();
1798                 } catch (Exception e) {
1799                     appendException(errors, e, "flushing log manager");
1800                 }
1801             }
1802 
1803             try {
1804                 fileManager.clear();
1805             } catch (Exception e) {
1806                 appendException(errors, e, "clearing file manager");
1807             }
1808 
1809             try {
1810                 fileManager.close();
1811             } catch (Exception e) {
1812                 appendException(errors, e, "closing file manager");
1813             }
1814 
1815             /*
1816              * Close the memory budgets on these components before the
1817              * INList is forcibly released and the treeAdmin budget is
1818              * cleared.
1819              */
1820             dbMapTree.close();
1821             cleaner.close();
1822             inMemoryINs.clear();
1823 
1824             closeHandlers();
1825 
1826             if (!isAbnormalClose &&
1827                 (envState != DbEnvState.INVALID)) {
1828 
1829                 try {
1830                     checkLeaks();
1831                 } catch (Exception e) {
1832                     appendException(errors, e, "performing validity checks");
1833                 }
1834             }
1835         } finally {
1836             envState = DbEnvState.CLOSED;
1837 
1838             /*
1839              * Last ditch effort to clean up so that tests can continue and
1840              * re-open the Environment in the face of an Exception or even an
1841              * Error.  Note that this was also attempted above.  [#21929]
1842              */
1843             clearFileManager();
1844             closeHandlers();
1845         }
1846 
1847         /* Don't whine again if we've already whined. */
1848         if (errorStringWriter.getBuffer().length() > 0 &&
1849             savedInvalidatingException == null) {
1850             throw EnvironmentFailureException.unexpectedState
1851                 (errorStringWriter.toString());
1852         }
1853     }
1854 
appendException(PrintWriter pw, Exception e, String doingWhat)1855     protected void appendException(PrintWriter pw,
1856                                    Exception e,
1857                                    String doingWhat) {
1858         pw.append("\nException " + doingWhat + ": ");
1859         e.printStackTrace(pw);
1860         pw.println();
1861     }
1862 
1863     /**
1864      * Release any resources from a subclass that need to be released before
1865      * close is called on regular environment components.
1866      * @throws DatabaseException
1867      */
setupClose(@uppressWarningsR) PrintWriter errors)1868     protected synchronized void setupClose(@SuppressWarnings("unused")
1869                                            PrintWriter errors)
1870         throws DatabaseException {
1871     }
1872 
1873     /**
1874      * Release any resources from a subclass that need to be released after
1875      * the closing checkpoint.
1876      * @param checkpointed if true, a checkpoint as issued before the close
1877      * @throws DatabaseException
1878      */
postCheckpointClose(boolean checkpointed)1879     protected synchronized void postCheckpointClose(boolean checkpointed)
1880         throws DatabaseException {
1881     }
1882 
1883     /**
1884      * Called after recovery but before any other initialization. Is overridden
1885      * by ReplImpl to convert user defined databases to replicated after doing
1886      * recovery.
1887      */
postRecoveryConversion()1888     protected void postRecoveryConversion() {
1889     }
1890 
1891     /**
1892      * Perform dup conversion after recovery and before running daemons.
1893      */
convertDupDatabases()1894     private void convertDupDatabases() {
1895         if (dbMapTree.getDupsConverted()) {
1896             return;
1897         }
1898         /* Convert dup dbs, set converted flag, flush mapping tree root. */
1899         final DupConvert dupConvert = new DupConvert(this, dbMapTree);
1900         dupConvert.convertDatabases();
1901         dbMapTree.setDupsConverted();
1902         logMapTreeRoot();
1903         logManager.flush();
1904     }
1905 
1906     /*
1907      * Clear as many resources as possible, even in the face of an environment
1908      * that has received a fatal error, in order to support reopening the
1909      * environment in the same JVM.
1910      */
closeAfterInvalid()1911     public void closeAfterInvalid()
1912         throws DatabaseException {
1913 
1914         /* Calls doCloseAfterInvalid while synchronized on DbEnvPool. */
1915         DbEnvPool.getInstance().closeEnvironmentAfterInvalid(this);
1916     }
1917 
1918     /**
1919      * This method must be called while synchronized on DbEnvPool.
1920      */
doCloseAfterInvalid()1921     public synchronized void doCloseAfterInvalid() {
1922 
1923         try {
1924             unregisterMBean();
1925         } catch (Exception e) {
1926             /* Klockwork - ok */
1927         }
1928 
1929         shutdownDaemons();
1930 
1931         try {
1932             fileManager.clear();
1933         } catch (Throwable e) {
1934             /* Klockwork - ok */
1935         }
1936 
1937         try {
1938             fileManager.close();
1939         } catch (Throwable e) {
1940             /* Klockwork - ok */
1941         }
1942 
1943         /*
1944          * Release resources held by handlers, such as memory and file
1945          * descriptors
1946          */
1947         closeHandlers();
1948     }
1949 
incOpenCount()1950     void incOpenCount() {
1951         openCount.incrementAndGet();
1952     }
1953 
1954     /**
1955      * Returns true if the environment should be closed.
1956      */
decOpenCount()1957     boolean decOpenCount() {
1958         return (openCount.decrementAndGet() <= 0);
1959     }
1960 
1961     /**
1962      * Returns a count of open environment handles, not including the internal
1963      * handle.
1964      */
getOpenCount()1965     private int getOpenCount() {
1966         return openCount.get();
1967     }
1968 
1969     /**
1970      * Returns the count of environment handles that were opened explicitly by
1971      * the application. Because the internal environment handle is not included
1972      * in the openCount, this method is currently equivalent to getOpenCount.
1973      *
1974      * @return the count of open application handles
1975      */
getAppOpenCount()1976     protected int getAppOpenCount() {
1977         return openCount.get();
1978     }
1979 
incBackupCount()1980     void incBackupCount() {
1981         backupCount.incrementAndGet();
1982     }
1983 
decBackupCount()1984     void decBackupCount() {
1985         backupCount.decrementAndGet();
1986     }
1987 
1988     /**
1989      * Returns a count of the number of in-progress DbBackups.
1990      */
getBackupCount()1991     protected int getBackupCount() {
1992         return backupCount.get();
1993     }
1994 
getThreadLocalReferenceCount()1995     public static int getThreadLocalReferenceCount() {
1996         return threadLocalReferenceCount;
1997     }
1998 
incThreadLocalReferenceCount()1999     static synchronized void incThreadLocalReferenceCount() {
2000         threadLocalReferenceCount++;
2001     }
2002 
decThreadLocalReferenceCount()2003     static synchronized void decThreadLocalReferenceCount() {
2004         threadLocalReferenceCount--;
2005     }
2006 
getDidFullThreadDump()2007     public boolean getDidFullThreadDump() {
2008         return didFullThreadDump;
2009     }
2010 
setDidFullThreadDump(boolean val)2011     public void setDidFullThreadDump(boolean val) {
2012         didFullThreadDump = val;
2013     }
2014 
getNoComparators()2015     public boolean getNoComparators() {
2016         return noComparators;
2017     }
2018 
2019     /**
2020      * Debugging support. Check for leaked locks and transactions.
2021      */
checkLeaks()2022     private void checkLeaks()
2023         throws DatabaseException {
2024 
2025         /* Only enabled if this check leak flag is true. */
2026         if (!configManager.getBoolean(EnvironmentParams.ENV_CHECK_LEAKS)) {
2027             return;
2028         }
2029 
2030         boolean clean = true;
2031         StatsConfig statsConfig = new StatsConfig();
2032 
2033         /* Fast stats will not return NTotalLocks below. */
2034         statsConfig.setFast(false);
2035 
2036         LockStats lockStat = lockStat(statsConfig);
2037         if (lockStat.getNTotalLocks() != 0) {
2038             clean = false;
2039             System.err.println("Problem: " + lockStat.getNTotalLocks() +
2040                                " locks left");
2041             txnManager.getLockManager().dump();
2042         }
2043 
2044         TransactionStats txnStat = txnStat(statsConfig);
2045         if (txnStat.getNActive() != 0) {
2046             clean = false;
2047             System.err.println("Problem: " + txnStat.getNActive() +
2048                                " txns left");
2049             TransactionStats.Active[] active = txnStat.getActiveTxns();
2050             if (active != null) {
2051                 for (Active element : active) {
2052                     System.err.println(element);
2053                 }
2054             }
2055         }
2056 
2057         if (LatchSupport.TRACK_LATCHES) {
2058             if (LatchSupport.nBtreeLatchesHeld() > 0) {
2059                 clean = false;
2060                 System.err.println("Some latches held at env close.");
2061                 System.err.println(LatchSupport.btreeLatchesHeldToString());
2062             }
2063         }
2064 
2065         long memoryUsage = memoryBudget.getVariableCacheUsage();
2066         if (memoryUsage != 0) {
2067             clean = false;
2068             System.err.println("Local Cache Usage = " + memoryUsage);
2069             System.err.println(memoryBudget.loadStats());
2070         }
2071 
2072         boolean assertionsEnabled = false;
2073         assert assertionsEnabled = true; // Intentional side effect.
2074         if (!clean && assertionsEnabled) {
2075             throw EnvironmentFailureException.unexpectedState
2076                 ("Lock, transaction, latch or memory " +
2077                  "left behind at environment close");
2078         }
2079     }
2080 
2081     /**
2082      * Invoke a checkpoint programmatically. Note that only one checkpoint may
2083      * run at a time.
2084      */
invokeCheckpoint(CheckpointConfig config, String invokingSource)2085     public boolean invokeCheckpoint(CheckpointConfig config,
2086                                     String invokingSource)
2087         throws DatabaseException {
2088 
2089         if (checkpointer != null) {
2090             checkpointer.doCheckpoint(config, invokingSource);
2091             return true;
2092         }
2093         return false;
2094     }
2095 
2096     /**
2097      * Flush the log buffers and write to the log, and optionally fsync.
2098      * [#19111]
2099      */
flushLog(boolean fsync)2100     public void flushLog(boolean fsync) {
2101         if (fsync) {
2102             logManager.flush();
2103         } else {
2104             logManager.flushWriteNoSync();
2105         }
2106     }
2107 
2108     /**
2109      * Flip the log to a new file, forcing an fsync.  Return the LSN of the
2110      * trace record in the new file.
2111      */
forceLogFileFlip()2112     public long forceLogFileFlip()
2113         throws DatabaseException {
2114 
2115         return logManager.logForceFlip(
2116             new TraceLogEntry(new Trace("File Flip")));
2117     }
2118 
2119     /**
2120      * Invoke a compress programatically. Note that only one compress may run
2121      * at a time.
2122      */
invokeCompressor()2123     public boolean invokeCompressor()
2124         throws DatabaseException {
2125 
2126         if (inCompressor != null) {
2127             inCompressor.doCompress();
2128             return true;
2129         }
2130         return false;
2131     }
2132 
invokeEvictor()2133     public void invokeEvictor()
2134         throws DatabaseException {
2135 
2136         if (evictor != null) {
2137             evictor.doManualEvict();
2138         }
2139     }
2140 
2141     /**
2142      * @throws UnsupportedOperationException via Environment.cleanLog.
2143      */
invokeCleaner()2144     public int invokeCleaner()
2145         throws DatabaseException {
2146 
2147         if (isReadOnly || isMemOnly) {
2148             throw new UnsupportedOperationException
2149                 ("Log cleaning not allowed in a read-only or memory-only " +
2150                  "environment");
2151         }
2152         if (cleaner != null) {
2153             return cleaner.doClean(true,   // cleanMultipleFiles
2154                                    false); // forceCleaning
2155         }
2156         return 0;
2157     }
2158 
requestShutdownDaemons()2159     public void requestShutdownDaemons() {
2160 
2161         closing = true;
2162 
2163         if (inCompressor != null) {
2164             inCompressor.requestShutdown();
2165         }
2166 
2167         /*
2168          * Don't shutdown the shared cache evictor here.  It is shutdown when
2169          * the last shared cache environment is removed in DbEnvPool.
2170          */
2171         if (evictor != null && !sharedCache) {
2172             evictor.requestShutdownPool();
2173         }
2174 
2175         if (checkpointer != null) {
2176             checkpointer.requestShutdown();
2177         }
2178 
2179         if (cleaner != null) {
2180             cleaner.requestShutdown();
2181         }
2182 
2183         if (statCapture != null) {
2184             statCapture.requestShutdown();
2185         }
2186     }
2187 
2188     /**
2189      * For unit testing -- shuts down daemons completely but leaves environment
2190      * usable since environment references are not nulled out.
2191      */
stopDaemons()2192     public void stopDaemons() {
2193 
2194         if (inCompressor != null) {
2195             inCompressor.shutdown();
2196         }
2197         if (evictor != null) {
2198             evictor.shutdown();
2199         }
2200         if (checkpointer != null) {
2201             checkpointer.shutdown();
2202         }
2203         if (cleaner != null) {
2204             cleaner.shutdown();
2205         }
2206         if (statCapture != null) {
2207             statCapture.shutdown();
2208         }
2209     }
2210 
2211     /**
2212      * Ask all daemon threads to shut down.
2213      */
shutdownDaemons()2214     public void shutdownDaemons() {
2215 
2216         /* Shutdown stats capture thread first so we can access stats. */
2217         shutdownStatCapture();
2218         synchronized (statSynchronizer) {
2219 
2220             shutdownINCompressor();
2221 
2222             /*
2223              * Cleaner has to be shutdown before checkpointer because former
2224              * calls the latter.
2225              */
2226             shutdownCleaner();
2227             shutdownCheckpointer();
2228 
2229             /*
2230              * The evictor has to get shutdown last because the other daemons
2231              * might create changes to the memory usage which result in a
2232              * notify to eviction.
2233              */
2234             shutdownEvictor();
2235         }
2236     }
2237 
shutdownINCompressor()2238     void shutdownINCompressor() {
2239         if (inCompressor != null) {
2240             inCompressor.shutdown();
2241 
2242             /*
2243              * If daemon thread doesn't shutdown for any reason, at least clear
2244              * the reference to the environment so it can be GC'd.
2245              */
2246             inCompressor.clearEnv();
2247             inCompressor = null;
2248         }
2249         return;
2250     }
2251 
shutdownEvictor()2252     void shutdownEvictor() {
2253         if (evictor != null) {
2254             if (sharedCache) {
2255 
2256                 /*
2257                  * Don't shutdown the SharedEvictor here.  It is shutdown when
2258                  * the last shared cache environment is removed in DbEnvPool.
2259                  * Instead, remove this environment from the SharedEvictor's
2260                  * list so we won't try to evict from a closing/closed
2261                  * environment.  Note that we do this after the final checkpoint
2262                  * so that eviction is possible during the checkpoint, and just
2263                  * before deconstructing the environment.  Leave the evictor
2264                  * field intact so DbEnvPool can get it.
2265                  */
2266                 evictor.removeEnvironment(this);
2267             } else {
2268                 evictor.shutdown();
2269                 evictor = null;
2270             }
2271         }
2272         return;
2273     }
2274 
shutdownCheckpointer()2275     void shutdownCheckpointer() {
2276         if (checkpointer != null) {
2277             checkpointer.shutdown();
2278 
2279             /*
2280              * If daemon thread doesn't shutdown for any reason, at least clear
2281              * the reference to the environment so it can be GC'd.
2282              */
2283             checkpointer.clearEnv();
2284             checkpointer = null;
2285         }
2286         return;
2287     }
2288 
shutdownStatCapture()2289     void shutdownStatCapture() {
2290         if (statCapture != null) {
2291             statCapture.shutdown();
2292 
2293             /*
2294              * If daemon thread doesn't shutdown for any reason, at least clear
2295              * the reference to the environment so it can be GC'd.
2296              */
2297             statCapture.clearEnv();
2298             statCapture = null;
2299         }
2300         return;
2301     }
2302 
2303     /*
2304      * Create stat capture if configured and not created yet.
2305      */
createStatCapture()2306     private void createStatCapture() {
2307         if (statCapture == null &&
2308             !isReadOnly() &&
2309             !isMemOnly() &&
2310             configManager.getBoolean(EnvironmentParams.STATS_COLLECT)) {
2311             statCapture =
2312                 new StatCapture(this,
2313                                 Environment.STATCAPTURE_NAME,
2314                                 configManager.getDuration(
2315                                     EnvironmentParams.STATS_COLLECT_INTERVAL),
2316                                 customStats,
2317                                 getStatCaptureProjections(),
2318                                 statManager);
2319        }
2320     }
2321 
2322     /**
2323      * public for unit tests.
2324      */
shutdownCleaner()2325     public void shutdownCleaner() {
2326 
2327         if (cleaner != null) {
2328             cleaner.shutdown();
2329 
2330             /*
2331              * Don't call clearEnv -- Cleaner.shutdown does this for each
2332              * cleaner thread.  Don't set the cleaner field to null because we
2333              * use it to get the utilization profile and tracker.
2334              */
2335         }
2336         return;
2337     }
2338 
isNoLocking()2339     public boolean isNoLocking() {
2340         return isNoLocking;
2341     }
2342 
isTransactional()2343     public boolean isTransactional() {
2344         return isTransactional;
2345     }
2346 
isReadOnly()2347     public boolean isReadOnly() {
2348         return isReadOnly;
2349     }
2350 
isMemOnly()2351     public boolean isMemOnly() {
2352         return isMemOnly;
2353     }
2354 
2355     /**
2356      * Named "optional" because the nodeName property in EnvironmentConfig is
2357      * optional may be null.  {@link #getName()} should almost always be used
2358      * instead for messages, exceptions, etc.
2359      */
getOptionalNodeName()2360     public String getOptionalNodeName() {
2361         return optionalNodeName;
2362     }
2363 
2364     /**
2365      * Returns whether DB/MapLN eviction is enabled.
2366      */
getDbEviction()2367     public boolean getDbEviction() {
2368         return dbEviction;
2369     }
2370 
getAdler32ChunkSize()2371     public static int getAdler32ChunkSize() {
2372         return adler32ChunkSize;
2373     }
2374 
getSharedCache()2375     public boolean getSharedCache() {
2376         return sharedCache;
2377     }
2378 
allowBlindOps()2379     public boolean allowBlindOps() {
2380         return allowBlindOps;
2381     }
2382 
allowBlindPuts()2383     public boolean allowBlindPuts() {
2384         return allowBlindPuts;
2385     }
2386 
2387     /**
2388      * Transactional services.
2389      */
txnBegin(Transaction parent, TransactionConfig txnConfig)2390     public Txn txnBegin(Transaction parent, TransactionConfig txnConfig)
2391         throws DatabaseException {
2392 
2393         return txnManager.txnBegin(parent, txnConfig);
2394     }
2395 
2396     /* Services. */
getLogManager()2397     public LogManager getLogManager() {
2398         return logManager;
2399     }
2400 
getFileManager()2401     public FileManager getFileManager() {
2402         return fileManager;
2403     }
2404 
getDbTree()2405     public DbTree getDbTree() {
2406         return dbMapTree;
2407     }
2408 
2409     /**
2410      * Returns the config manager for the current base configuration.
2411      *
2412      * <p>The configuration can change, but changes are made by replacing the
2413      * config manager object with a enw one.  To use a consistent set of
2414      * properties, call this method once and query the returned manager
2415      * repeatedly for each property, rather than getting the config manager via
2416      * this method for each property individually.</p>
2417      */
getConfigManager()2418     public DbConfigManager getConfigManager() {
2419         return configManager;
2420     }
2421 
getNodeSequence()2422     public NodeSequence getNodeSequence() {
2423         return nodeSequence;
2424     }
2425 
2426     /**
2427      * Clones the current configuration.
2428      */
cloneConfig()2429     public EnvironmentConfig cloneConfig() {
2430         return configManager.getEnvironmentConfig().clone();
2431     }
2432 
2433     /**
2434      * Clones the current mutable configuration.
2435      */
cloneMutableConfig()2436     public EnvironmentMutableConfig cloneMutableConfig() {
2437         return DbInternal.cloneMutableConfig
2438             (configManager.getEnvironmentConfig());
2439     }
2440 
2441     /**
2442      * Throws an exception if an immutable property is changed.
2443      */
checkImmutablePropsForEquality(Properties handleConfigProps)2444     public void checkImmutablePropsForEquality(Properties handleConfigProps)
2445         throws IllegalArgumentException {
2446 
2447         DbInternal.checkImmutablePropsForEquality
2448             (configManager.getEnvironmentConfig(), handleConfigProps);
2449     }
2450 
2451     /**
2452      * Changes the mutable config properties that are present in the given
2453      * config, and notifies all config observer.
2454      */
setMutableConfig(EnvironmentMutableConfig config)2455     public void setMutableConfig(EnvironmentMutableConfig config)
2456         throws DatabaseException {
2457 
2458         /* Calls doSetMutableConfig while synchronized on DbEnvPool. */
2459         DbEnvPool.getInstance().setMutableConfig(this, config);
2460     }
2461 
2462     /**
2463      * This method must be called while synchronized on DbEnvPool.
2464      */
doSetMutableConfig(EnvironmentMutableConfig config)2465     synchronized void doSetMutableConfig(EnvironmentMutableConfig config)
2466         throws DatabaseException {
2467 
2468         /* Clone the current config. */
2469         EnvironmentConfig newConfig =
2470             configManager.getEnvironmentConfig().clone();
2471 
2472         /* Copy in the mutable props. */
2473         DbInternal.copyMutablePropsTo(config, newConfig);
2474 
2475         /*
2476          * Update the current config and notify observers.  The config manager
2477          * is replaced with a new instance that uses the new configuration.
2478          * This avoids synchronization issues: other threads that have a
2479          * reference to the old configuration object are not impacted.
2480          *
2481          * Notify listeners in reverse order of registration so that the
2482          * environment listener is notified last and can start daemon threads
2483          * after they are configured.
2484          */
2485         configManager = resetConfigManager(newConfig);
2486         for (int i = configObservers.size() - 1; i >= 0; i -= 1) {
2487             EnvConfigObserver o = configObservers.get(i);
2488             o.envConfigUpdate(configManager, newConfig);
2489         }
2490     }
2491 
2492     /**
2493      * Make a new config manager that has all the properties needed. More
2494      * complicated for subclasses.
2495      */
resetConfigManager(EnvironmentConfig newConfig)2496     protected DbConfigManager resetConfigManager(EnvironmentConfig newConfig) {
2497         return new DbConfigManager(newConfig);
2498     }
2499 
getExceptionListener()2500     public ExceptionListener getExceptionListener() {
2501         return exceptionListener;
2502     }
2503 
2504     /**
2505      * Adds an observer of mutable config changes.
2506      */
addConfigObserver(EnvConfigObserver o)2507     public synchronized void addConfigObserver(EnvConfigObserver o) {
2508         configObservers.add(o);
2509     }
2510 
2511     /**
2512      * Removes an observer of mutable config changes.
2513      */
removeConfigObserver(EnvConfigObserver o)2514     public synchronized void removeConfigObserver(EnvConfigObserver o) {
2515         configObservers.remove(o);
2516     }
2517 
getInMemoryINs()2518     public INList getInMemoryINs() {
2519         return inMemoryINs;
2520     }
2521 
getTxnManager()2522     public TxnManager getTxnManager() {
2523         return txnManager;
2524     }
2525 
getCheckpointer()2526     public Checkpointer getCheckpointer() {
2527         return checkpointer;
2528     }
2529 
getCleaner()2530     public Cleaner getCleaner() {
2531         return cleaner;
2532     }
2533 
getMemoryBudget()2534     public MemoryBudget getMemoryBudget() {
2535         return memoryBudget;
2536     }
2537 
2538     /**
2539      * @return environment Logger, for use in debugging output.
2540      */
getLogger()2541     public Logger getLogger() {
2542         return envLogger;
2543     }
2544 
isDbLoggingDisabled()2545     public boolean isDbLoggingDisabled() {
2546         return dbLoggingDisabled;
2547     }
2548 
2549     /*
2550      * Verification, must be run while system is quiescent.
2551      */
verify(VerifyConfig config, PrintStream out)2552     public boolean verify(VerifyConfig config, PrintStream out)
2553         throws DatabaseException {
2554 
2555         /* For now, verify all databases */
2556         return dbMapTree.verify(config, out);
2557     }
2558 
verifyCursors()2559     public void verifyCursors()
2560         throws DatabaseException {
2561 
2562         inCompressor.verifyCursors();
2563     }
2564 
2565     /*
2566      * Statistics
2567      */
2568 
2569     /**
2570      * Retrieve and return stat information.
2571      */
loadStats(StatsConfig config)2572     public EnvironmentStats loadStats(StatsConfig config)
2573         throws DatabaseException {
2574         return statManager.loadStats(config, statKey);
2575     }
2576 
2577     /**
2578      * Retrieve and return stat information.
2579      */
loadStatsInternal(StatsConfig config)2580     public EnvironmentStats loadStatsInternal(StatsConfig config)
2581         throws DatabaseException {
2582 
2583         EnvironmentStats envStats = new EnvironmentStats();
2584 
2585         synchronized (statSynchronizer) {
2586             envStats.setINCompStats(inCompressor.loadStats(config));
2587             envStats.setCkptStats(checkpointer.loadStats(config));
2588             envStats.setCleanerStats(cleaner.loadStats(config));
2589             envStats.setLogStats(logManager.loadStats(config));
2590             envStats.setMBAndEvictorStats(memoryBudget.loadStats(),
2591                                           evictor.loadStats(config));
2592             envStats.setLockStats(txnManager.loadStats(config));
2593             envStats.setEnvImplStats(loadEnvImplStats(config));
2594             envStats.setStatGroup(thrputStats.cloneGroup(config.getClear()));
2595         }
2596         return envStats;
2597     }
2598 
loadEnvImplStats(StatsConfig config)2599     public StatGroup loadEnvImplStats(StatsConfig config) {
2600         StatGroup ret = stats.cloneGroup(config.getClear());
2601         LongStat ct = new LongStat(ret, ENVIMPL_CREATION_TIME);
2602         ct.set(creationTime);
2603         return ret;
2604 
2605     }
2606 
incRelatchesRequired()2607     public void incRelatchesRequired() {
2608         relatchesRequired.increment();
2609     }
2610 
2611     /**
2612      * For replicated environments only; just return true for a standalone
2613      * environment.
2614      */
addDbBackup(@uppressWarningsR) DbBackup backup)2615     public boolean addDbBackup(@SuppressWarnings("unused") DbBackup backup) {
2616         incBackupCount();
2617         return true;
2618     }
2619 
2620     /**
2621      * For replicated environments only; do nothing for a standalone
2622      * environment.
2623      */
removeDbBackup(@uppressWarningsR) DbBackup backup)2624     public void removeDbBackup(@SuppressWarnings("unused") DbBackup backup) {
2625         decBackupCount();
2626     }
2627 
2628     /**
2629      * Retrieve lock statistics
2630      */
lockStat(StatsConfig config)2631     public synchronized LockStats lockStat(StatsConfig config)
2632         throws DatabaseException {
2633 
2634         return txnManager.lockStat(config);
2635     }
2636 
2637     /**
2638      * Retrieve txn statistics
2639      */
txnStat(StatsConfig config)2640     public synchronized TransactionStats txnStat(StatsConfig config) {
2641         return txnManager.txnStat(config);
2642     }
2643 
getINCompressorQueueSize()2644     public int getINCompressorQueueSize() {
2645         return inCompressor.getBinRefQueueSize();
2646     }
2647 
getStartupTracker()2648     public StartupTracker getStartupTracker() {
2649         return startupTracker;
2650     }
2651 
2652     /**
2653      * Get the environment home directory.
2654      */
getEnvironmentHome()2655     public File getEnvironmentHome() {
2656         return envHome;
2657     }
2658 
getInternalEnvHandle()2659     public Environment getInternalEnvHandle() {
2660         return envInternal;
2661     }
2662 
2663     /**
2664      * Closes the internally maintained environment handle. If the close is
2665      * an abnormal close, it just does cleanup work instead of trying to close
2666      * the internal environment handle which may result in further errors.
2667      */
closeInternalEnvHandle(boolean isAbnormalClose)2668     private synchronized void closeInternalEnvHandle(boolean isAbnormalClose) {
2669 
2670         if (envInternal == null) {
2671             return;
2672         }
2673 
2674         if (isAbnormalClose) {
2675             envInternal = null;
2676         } else {
2677             final Environment savedEnvInternal = envInternal;
2678             /* Blocks recursions resulting from the close operation below */
2679             envInternal = null;
2680             DbInternal.closeInternalHandle(savedEnvInternal);
2681         }
2682     }
2683 
2684     /**
2685      * Get an environment name, for tagging onto logging and debug message.
2686      * Useful for multiple environments in a JVM, or for HA.
2687      */
getName()2688     public String getName() {
2689         if (optionalNodeName == null){
2690             return envHome.toString();
2691         }
2692         return getOptionalNodeName();
2693     }
2694 
getTxnTimeout()2695     public long getTxnTimeout() {
2696         return txnTimeout;
2697     }
2698 
getLockTimeout()2699     public long getLockTimeout() {
2700         return lockTimeout;
2701     }
2702 
getReplayTxnTimeout()2703     public long getReplayTxnTimeout() {
2704         if (lockTimeout != 0) {
2705             return lockTimeout;
2706         }
2707         /* It can't be disabled, so make it the minimum. */
2708         return 1;
2709     }
2710 
2711     /**
2712      * Returns the shared secondary association latch.
2713      */
getSecondaryAssociationLock()2714     public ReentrantReadWriteLock getSecondaryAssociationLock() {
2715         return secondaryAssociationLock;
2716     }
2717 
getEvictor()2718     public Evictor getEvictor() {
2719         return evictor;
2720     }
2721 
alertEvictor()2722     void alertEvictor() {
2723         if (evictor != null) {
2724             evictor.alert();
2725         }
2726     }
2727 
2728     /**
2729      * Performs critical eviction if necessary.  Is called before and after
2730      * each cursor operation. We prefer to have the application thread do as
2731      * little eviction as possible, to reduce the impact on latency, so
2732      * critical eviction has an explicit set of criteria for determining when
2733      * this should run.
2734      *
2735      * WARNING: The action performed here should be as inexpensive as possible,
2736      * since it will impact app operation latency.  Unconditional
2737      * synchronization must not be performed, since that would introduce a new
2738      * synchronization point for all app threads.
2739      *
2740      * An overriding method must call super.criticalEviction.
2741      *
2742      * No latches are held or synchronization is in use when this method is
2743      * called.
2744      */
criticalEviction(boolean backgroundIO)2745     public void criticalEviction(boolean backgroundIO) {
2746         evictor.doCriticalEviction(backgroundIO);
2747     }
2748 
2749     /**
2750      * Do eviction if the memory budget is over. Called by JE daemon
2751      * threads that do not have the same latency concerns as application
2752      * threads.
2753      */
daemonEviction(boolean backgroundIO)2754     public void daemonEviction(boolean backgroundIO) {
2755         evictor.doDaemonEviction(backgroundIO);
2756     }
2757 
2758     /**
2759      * Performs special eviction (eviction other than standard IN eviction)
2760      * for this environment.  This method is called once per eviction batch to
2761      * give other components an opportunity to perform eviction.  For a shared
2762      * cached, it is called for only one environment (in rotation) per batch.
2763      *
2764      * An overriding method must call super.specialEviction and return the sum
2765      * of the long value it returns and any additional amount of budgeted
2766      * memory that is evicted.
2767      *
2768      * No latches are held when this method is called, but it is called while
2769      * synchronized on the evictor.
2770      *
2771      * @return the number of bytes evicted from the JE cache.
2772      */
specialEviction()2773     public long specialEviction() {
2774         return cleaner.getUtilizationTracker().evictMemory();
2775     }
2776 
2777     /**
2778      * See Evictor.isCacheFull
2779      */
isCacheFull()2780     public boolean isCacheFull() {
2781         return getEvictor().isCacheFull();
2782     }
2783 
2784     /**
2785      * See Evictor.wasCacheEverFull
2786      */
wasCacheEverFull()2787     public boolean wasCacheEverFull() {
2788         return getEvictor().wasCacheEverFull();
2789     }
2790 
2791     /**
2792      * For stress testing.  Should only ever be called from an assert.
2793      */
maybeForceYield()2794     public static boolean maybeForceYield() {
2795         if (forcedYield) {
2796             Thread.yield();
2797         }
2798         return true;      // so assert doesn't fire
2799     }
2800 
2801     /**
2802      * Return true if this environment is part of a replication group.
2803      */
isReplicated()2804     public boolean isReplicated() {
2805         return false;
2806     }
2807 
2808     /**
2809      * Returns true if the VLSN is preserved as the record version.  Always
2810      * false in a standalone environment.  Overridden by RepImpl.
2811      */
getPreserveVLSN()2812     public boolean getPreserveVLSN() {
2813         return false;
2814     }
2815 
2816     /**
2817      * Returns true if the VLSN is both preserved and cached.  Always false in
2818      * a standalone environment.  Overridden by RepImpl.
2819      */
getCacheVLSN()2820     public boolean getCacheVLSN() {
2821         return false;
2822     }
2823 
2824     /**
2825      * Returns the number of initial bytes per VLSN in the vlsnCache. Returns
2826      * zero in a standalone env.  Overridden by RepImpl.
2827      */
getCachedVLSNMinLength()2828     public int getCachedVLSNMinLength() {
2829         return 0;
2830     }
2831 
2832     /**
2833      * True if ReplicationConfig set allowConvert as true. Standalone
2834      * environment is prohibited from doing a conversion, return false.
2835      */
getAllowRepConvert()2836     public boolean getAllowRepConvert() {
2837         return false;
2838     }
2839 
2840     /**
2841      * True if this environment is converted from non-replicated to
2842      * replicated.
2843      */
isRepConverted()2844     public boolean isRepConverted() {
2845         return dbMapTree.isRepConverted();
2846     }
2847 
needRepConvert()2848     public boolean needRepConvert() {
2849         return needRepConvert;
2850     }
2851 
bumpVLSN()2852     public VLSN bumpVLSN() {
2853         /* NOP for non-replicated environment. */
2854         return null;
2855     }
2856 
decrementVLSN()2857     public void decrementVLSN() {
2858         /* NOP for non-replicated environment. */
2859     }
2860 
2861     /**
2862      * @throws DatabaseException from subclasses.
2863      */
getVLSNProxy()2864     public VLSNRecoveryProxy getVLSNProxy()
2865         throws DatabaseException {
2866 
2867         return new NoopVLSNProxy();
2868     }
2869 
isMaster()2870     public boolean isMaster() {
2871         /* NOP for non-replicated environment. */
2872         return false;
2873     }
2874 
2875     /**
2876      * @param recoveryInfo
2877      */
preRecoveryCheckpointInit(RecoveryInfo recoveryInfo)2878     public void preRecoveryCheckpointInit(RecoveryInfo recoveryInfo) {
2879         /* NOP for non-replicated environment. */
2880     }
2881 
2882     /**
2883      * @param logItem
2884      */
registerVLSN(LogItem logItem)2885     public void registerVLSN(LogItem logItem) {
2886         /* NOP for non-replicated environment. */
2887     }
2888 
2889     /**
2890      * Adjust the vlsn index after cleaning.
2891      * @param lastVLSN
2892      * @param deleteFileNum
2893      */
vlsnHeadTruncate(VLSN lastVLSN, long deleteFileNum)2894     public void vlsnHeadTruncate(VLSN lastVLSN, long deleteFileNum) {
2895 
2896         /* NOP for non-replicated environment. */
2897     }
2898 
2899     /**
2900      * Do any work that must be done before the checkpoint end is written, as
2901      * as part of the checkpoint process.
2902      * @throws DatabaseException
2903      */
preCheckpointEndFlush()2904     public void preCheckpointEndFlush()
2905         throws DatabaseException {
2906 
2907         /* NOP for non-replicated environment. */
2908     }
2909 
2910     /**
2911      * For replicated environments only; only the overridden method should
2912      * ever be called.
2913      * @param txnId
2914      * @throws DatabaseException from subclasses.
2915      */
createReplayTxn(long txnId)2916     public Txn createReplayTxn(long txnId) {
2917         throw EnvironmentFailureException.unexpectedState
2918             ("Should not be called on a non replicated environment");
2919     }
2920 
2921     /**
2922      * For replicated environments only; only the overridden method should
2923      * ever be called.
2924      * @throws DatabaseException from subclasses.
2925      */
createRepThreadLocker()2926     public ThreadLocker createRepThreadLocker() {
2927         throw EnvironmentFailureException.unexpectedState
2928             ("Should not be called on a non replicated environment");
2929     }
2930 
2931     /**
2932      * For replicated environments only; only the overridden method should
2933      * ever be called.
2934      * @param config
2935      * @throws DatabaseException from subclasses.
2936      */
createRepUserTxn(TransactionConfig config)2937     public Txn createRepUserTxn(TransactionConfig config) {
2938         throw EnvironmentFailureException.unexpectedState
2939             ("Should not be called on a non replicated environment");
2940     }
2941 
2942     /**
2943      * For replicated environments only; only the overridden method should
2944      * ever be called.
2945      * @param config
2946      * @param mandatedId
2947      * @throws DatabaseException from subclasses.
2948      */
createRepTxn(TransactionConfig config, long mandatedId)2949     public Txn createRepTxn(TransactionConfig config,
2950                             long mandatedId) {
2951         throw EnvironmentFailureException.unexpectedState
2952             ("Should not be called on a non replicated environment");
2953     }
2954 
2955     /**
2956      * For replicated environments only; only the overridden method should
2957      * ever be called.
2958      * @param locker
2959      * @param cause
2960      * @throws com.sleepycat.je.rep.LockPreemptedException from subclasses.
2961      */
2962     public OperationFailureException
createLockPreemptedException(Locker locker, Throwable cause)2963         createLockPreemptedException(Locker locker, Throwable cause) {
2964         throw EnvironmentFailureException.unexpectedState
2965             ("Should not be called on a non replicated environment");
2966     }
2967 
2968     /**
2969      * For replicated environments only; only the overridden method should
2970      * ever be called.
2971      * @param msg
2972      * @param dbName
2973      * @param db
2974      * @throws com.sleepycat.je.rep.DatabasePreemptedException from subclasses.
2975      */
2976     public OperationFailureException
createDatabasePreemptedException(String msg, String dbName, Database db)2977         createDatabasePreemptedException(String msg,
2978                                          String dbName,
2979                                          Database db) {
2980         throw EnvironmentFailureException.unexpectedState
2981             ("Should not be called on a non replicated environment");
2982     }
2983 
2984     /**
2985      * For replicated environments only; only the overridden method should
2986      * ever be called.
2987      * @param msg unused
2988      * @throws com.sleepycat.je.rep.LogOverwriteException from subclasses.
2989      */
createLogOverwriteException(String msg)2990     public OperationFailureException createLogOverwriteException(String msg) {
2991         throw EnvironmentFailureException.unexpectedState
2992             ("Should not be called on a non replicated environment");
2993     }
2994 
2995     /**
2996      * Removes files that ought to be protected from deletion.
2997      *
2998      * @param files a set of log file numbers that the Cleaner is contemplating
2999      * deleting.  This should of course be the set of files that the Cleaner
3000      * has decided are no longer needed for recovery and for holding database
3001      * contents.
3002      *
3003      * @return a set representing files that remain unprotected from deletion,
3004      * either a new set or a view onto the original set.  (This method does not
3005      * modify the original passed-in set.)  Returns null (in the RepImpl
3006      * subclass) if file deletion is prohibited.
3007      */
getUnprotectedFileSet( final NavigableSet<Long> files)3008     public NavigableSet<Long> getUnprotectedFileSet(
3009         final NavigableSet<Long> files) {
3010 
3011         /* Allow test hook to return barrier value. */
3012         if (cleanerBarrierHoook != null) {
3013             return files.headSet(cleanerBarrierHoook.getHookValue(), false);
3014         }
3015 
3016         return files;
3017     }
3018 
3019     /**
3020      * Check whether this environment can be opened on an existing environment
3021      * directory.
3022      * @param dbTreePreserveVLSN
3023      *
3024      * @throws UnsupportedOperationException via Environment ctor.
3025      */
checkRulesForExistingEnv(boolean dbTreeReplicatedBit, boolean dbTreePreserveVLSN)3026     public void checkRulesForExistingEnv(boolean dbTreeReplicatedBit,
3027                                          boolean dbTreePreserveVLSN)
3028         throws UnsupportedOperationException {
3029 
3030         /*
3031          * We only permit standalone Environment construction on an existing
3032          * environment when we are in read only mode, to support command
3033          * line utilities. We prohibit read/write opening, because we don't
3034          * want to chance corruption of the environment by writing non-VLSN
3035          * tagged entries in.
3036          */
3037         if (dbTreeReplicatedBit && (!isReadOnly())) {
3038             throw new UnsupportedOperationException
3039                 ("This environment was previously opened for replication." +
3040                  " It cannot be re-opened for in read/write mode for" +
3041                  " non-replicated operation.");
3042         }
3043 
3044         /*
3045          * Same as above but for the preserve VLSN param, which may only be
3046          * used in a replicated environment.  See this overridden method in
3047          * RepImpl which checks that the param is never changed.
3048          */
3049         if (getPreserveVLSN() && (!isReadOnly())) {
3050             /* Cannot use RepParams constant in standalone code. */
3051             throw new IllegalArgumentException
3052                 (EnvironmentParams.REP_PARAM_PREFIX +
3053                  "preserveRecordVersion parameter may not be true in a" +
3054                  " read-write, non-replicated environment");
3055         }
3056     }
3057 
3058     /**
3059      * Ensure that the in-memory vlsn index encompasses all logged entries
3060      * before it is flushed to disk. A No-Op for non-replicated systems.
3061      * [#19754]
3062      */
awaitVLSNConsistency()3063     public void awaitVLSNConsistency() {
3064         /* Nothing to do in a non-replicated system. */
3065     }
3066 
3067     /**
3068      * The VLSNRecoveryProxy is only needed for replicated environments.
3069      */
3070     private class NoopVLSNProxy implements VLSNRecoveryProxy {
3071 
3072         @Override
trackMapping(long lsn, LogEntryHeader currentEntryHeader, LogEntry targetLogEntry)3073         public void trackMapping(long lsn,
3074                                  LogEntryHeader currentEntryHeader,
3075                                  LogEntry targetLogEntry) {
3076             /* intentional no-op */
3077         }
3078     }
3079 
getThroughputStat(StatDefinition def)3080     public AtomicLongStat getThroughputStat(StatDefinition def) {
3081         return thrputStats.getAtomicLongStat(def);
3082     }
3083 
getThroughputStatGroup()3084     public ThroughputStatGroup getThroughputStatGroup() {
3085         return thrputStats;
3086     }
3087 
3088     /**
3089      * Private class to prevent used of the close() method by the application
3090      * on an internal handle.
3091      */
3092     private static class InternalEnvironment extends Environment {
3093 
InternalEnvironment(File envHome, EnvironmentConfig configuration, EnvironmentImpl envImpl)3094         public InternalEnvironment(File envHome,
3095                                    EnvironmentConfig configuration,
3096                                    EnvironmentImpl envImpl)
3097             throws EnvironmentNotFoundException,
3098                    EnvironmentLockedException,
3099                    VersionMismatchException,
3100                    DatabaseException,
3101                    IllegalArgumentException {
3102             super(envHome, configuration, false /*openIfNeeded*/,
3103                   null /*repConfigProxy*/, envImpl);
3104         }
3105 
3106         @Override
isInternalHandle()3107         protected boolean isInternalHandle() {
3108             return true;
3109         }
3110 
3111         @Override
close()3112         public synchronized void close() {
3113             throw EnvironmentFailureException.unexpectedState
3114                 ("close() not permitted on an internal environment handle");
3115         }
3116     }
3117 
3118     /**
3119      * Preload exceptions, classes.
3120      */
3121 
3122     /**
3123      * Undeclared exception used to throw through SortedLSNTreeWalker code
3124      * when preload has either filled the user's max byte or time request.
3125      */
3126     @SuppressWarnings("serial")
3127     private static class HaltPreloadException extends RuntimeException {
3128 
3129         private final PreloadStatus status;
3130 
HaltPreloadException(PreloadStatus status)3131         HaltPreloadException(PreloadStatus status) {
3132             super(status.toString());
3133             this.status = status;
3134         }
3135 
getStatus()3136         PreloadStatus getStatus() {
3137             return status;
3138         }
3139     }
3140 
3141     private static final HaltPreloadException
3142         TIME_EXCEEDED_PRELOAD_EXCEPTION =
3143         new HaltPreloadException(PreloadStatus.EXCEEDED_TIME);
3144 
3145     private static final HaltPreloadException
3146         MEMORY_EXCEEDED_PRELOAD_EXCEPTION =
3147         new HaltPreloadException(PreloadStatus.FILLED_CACHE);
3148 
3149     private static final HaltPreloadException
3150         USER_HALT_REQUEST_PRELOAD_EXCEPTION =
3151         new HaltPreloadException(PreloadStatus.USER_HALT_REQUEST);
3152 
preload(final DatabaseImpl[] dbImpls, final PreloadConfig config)3153     public PreloadStats preload(final DatabaseImpl[] dbImpls,
3154                                 final PreloadConfig config)
3155         throws DatabaseException {
3156 
3157         try {
3158             long maxBytes = config.getMaxBytes();
3159             long maxMillisecs = config.getMaxMillisecs();
3160             long targetTime = Long.MAX_VALUE;
3161             if (maxMillisecs > 0) {
3162                 targetTime = System.currentTimeMillis() + maxMillisecs;
3163                 if (targetTime < 0) {
3164                     targetTime = Long.MAX_VALUE;
3165                 }
3166             }
3167 
3168             long cacheBudget = getMemoryBudget().getMaxMemory();
3169             if (maxBytes == 0) {
3170                 maxBytes = cacheBudget;
3171             } else if (maxBytes > cacheBudget) {
3172                 throw new IllegalArgumentException
3173                     ("maxBytes parameter to preload() was " +
3174                      "specified as " +
3175                      maxBytes + " bytes \nbut the cache is only " +
3176                      cacheBudget + " bytes.");
3177             }
3178 
3179             /*
3180              * Sort DatabaseImpls so that we always latch in a well-defined
3181              * order to avoid potential deadlocks if multiple preloads happen
3182              * to (accidentally) execute concurrently.
3183              */
3184             Arrays.sort(dbImpls, new Comparator<DatabaseImpl>() {
3185                     @Override
3186                     public int compare(DatabaseImpl o1, DatabaseImpl o2) {
3187                         DatabaseId id1 = o1.getId();
3188                         DatabaseId id2 = o2.getId();
3189                         return id1.compareTo(id2);
3190                     }
3191                 });
3192 
3193             PreloadStats pstats = new PreloadStats();
3194             PreloadProcessor callback = new PreloadProcessor
3195                 (this, maxBytes, targetTime, pstats, config);
3196             int nDbs = dbImpls.length;
3197             long[] rootLsns = new long[nDbs];
3198             for (int i = 0; i < nDbs; i += 1) {
3199                 rootLsns[i] = dbImpls[i].getTree().getRootLsn();
3200             }
3201             SortedLSNTreeWalker walker =
3202                 new PreloadLSNTreeWalker(dbImpls, rootLsns, callback, config);
3203             try {
3204                 walker.walk();
3205                 callback.close();
3206             } catch (HaltPreloadException HPE) {
3207                 pstats.setStatus(HPE.getStatus());
3208             }
3209 
3210             if (LatchSupport.TRACK_LATCHES) {
3211                 LatchSupport.expectBtreeLatchesHeld(0);
3212             }
3213             return pstats;
3214         } catch (Error E) {
3215             invalidate(E);
3216             throw E;
3217         }
3218     }
3219 
3220     /**
3221      * The processLSN() code for PreloadLSNTreeWalker.
3222      */
3223     private static class PreloadProcessor implements TreeNodeProcessor {
3224 
3225         private final EnvironmentImpl envImpl;
3226         private final long maxBytes;
3227         private final long targetTime;
3228         private final PreloadStats stats;
3229         private final boolean countLNs;
3230         private final ProgressListener<PreloadConfig.Phases> progressListener;
3231         private long progressCounter = 0;
3232 
PreloadProcessor(final EnvironmentImpl envImpl, final long maxBytes, final long targetTime, final PreloadStats stats, final PreloadConfig config)3233         PreloadProcessor(final EnvironmentImpl envImpl,
3234                          final long maxBytes,
3235                          final long targetTime,
3236                          final PreloadStats stats,
3237                          final PreloadConfig config) {
3238             this.envImpl = envImpl;
3239             this.maxBytes = maxBytes;
3240             this.targetTime = targetTime;
3241             this.stats = stats;
3242             this.countLNs = config.getLoadLNs();
3243             this.progressListener = config.getProgressListener();
3244         }
3245 
3246         /**
3247          * Called for each LSN that the SortedLSNTreeWalker encounters.
3248          */
3249         @Override
processLSN(@uppressWarningsR) long childLsn, LogEntryType childType, @SuppressWarnings(R) Node ignore, @SuppressWarnings(R) byte[] ignore2, @SuppressWarnings(R) int ignore3)3250         public void processLSN(@SuppressWarnings("unused") long childLsn,
3251                                LogEntryType childType,
3252                                @SuppressWarnings("unused") Node ignore,
3253                                @SuppressWarnings("unused") byte[] ignore2,
3254                                @SuppressWarnings("unused") int ignore3) {
3255 
3256             /*
3257              * Check if we've exceeded either the max time or max bytes
3258              * allowed for this preload() call.
3259              */
3260             if (System.currentTimeMillis() > targetTime) {
3261                 throw TIME_EXCEEDED_PRELOAD_EXCEPTION;
3262             }
3263 
3264             if (envImpl.getMemoryBudget().getCacheMemoryUsage() > maxBytes) {
3265                 throw MEMORY_EXCEEDED_PRELOAD_EXCEPTION;
3266             }
3267 
3268             if (progressListener != null) {
3269                 progressCounter += 1;
3270                 if (!progressListener.progress(PreloadConfig.Phases.PRELOAD,
3271                                                progressCounter, -1)) {
3272                     throw USER_HALT_REQUEST_PRELOAD_EXCEPTION;
3273                 }
3274             }
3275 
3276             /* Count entry types to return in the PreloadStats. */
3277             if (childType.equals(LogEntryType.LOG_DUPCOUNTLN_TRANSACTIONAL) ||
3278                 childType.equals(LogEntryType.LOG_DUPCOUNTLN)) {
3279                 stats.incDupCountLNsLoaded();
3280             } else if (childType.isLNType()) {
3281                 if (countLNs) {
3282                     stats.incLNsLoaded();
3283                 }
3284             } else if (childType.equals(LogEntryType.LOG_DBIN)) {
3285                 stats.incDBINsLoaded();
3286             } else if (childType.equals(LogEntryType.LOG_BIN)) {
3287                 stats.incBINsLoaded();
3288             } else if (childType.equals(LogEntryType.LOG_DIN)) {
3289                 stats.incDINsLoaded();
3290             } else if (childType.equals(LogEntryType.LOG_IN)) {
3291                 stats.incINsLoaded();
3292             }
3293         }
3294 
3295         @Override
processDirtyDeletedLN(@uppressWarningsR) long childLsn, @SuppressWarnings(R) LN ln, @SuppressWarnings(R) byte[] lnKey)3296         public void processDirtyDeletedLN(@SuppressWarnings("unused")
3297                                           long childLsn,
3298                                           @SuppressWarnings("unused")
3299                                           LN ln,
3300                                           @SuppressWarnings("unused")
3301                                           byte[] lnKey) {
3302         }
3303 
3304         @Override
noteMemoryExceeded()3305         public void noteMemoryExceeded() {
3306             stats.incMemoryExceeded();
3307         }
3308 
close()3309         public void close() {
3310             /* Indicate that we're finished. */
3311             if (progressListener != null) {
3312                 progressListener.progress(PreloadConfig.Phases.PRELOAD,
3313                                           progressCounter, progressCounter);
3314             }
3315         }
3316     }
3317 
3318     /*
3319      * An extension of SortedLSNTreeWalker that latches the root IN.
3320      */
3321     private class PreloadLSNTreeWalker extends SortedLSNTreeWalker {
3322 
PreloadLSNTreeWalker(DatabaseImpl[] dbs, long[] rootLsns, TreeNodeProcessor callback, PreloadConfig conf)3323         PreloadLSNTreeWalker(DatabaseImpl[] dbs,
3324                              long[] rootLsns,
3325                              TreeNodeProcessor callback,
3326                              PreloadConfig conf)
3327             throws DatabaseException {
3328 
3329             super(dbs,
3330                   false /*setDbState*/,
3331                   rootLsns,
3332                   callback,
3333                   null, null); /* savedException, exception predicate */
3334             accumulateLNs = conf.getLoadLNs();
3335             setLSNBatchSize(conf.getLSNBatchSize());
3336             setInternalMemoryLimit(conf.getInternalMemoryLimit());
3337         }
3338 
3339         @Override
walk()3340         public void walk()
3341             throws DatabaseException {
3342 
3343             int nDbs = dbImpls.length;
3344             int nDbsLatched = 0;
3345             try {
3346                 try {
3347                     for (int i = 0; i < nDbs; i += 1) {
3348                         DatabaseImpl dbImpl = dbImpls[i];
3349                         dbImpl.getTree().latchRootLatchExclusive();
3350                         nDbsLatched += 1;
3351                     }
3352                 } catch (Exception e) {
3353                     throw EnvironmentFailureException.unexpectedException
3354                         (EnvironmentImpl.this,
3355                          "Couldn't latch all DatabaseImpls during preload", e);
3356                 }
3357 
3358                 walkInternal();
3359             } finally {
3360 
3361                 /*
3362                  * Release latches in reverse acquisition order to avoid
3363                  * deadlocks with possible concurrent preload operations.
3364                  */
3365                 for (int i = nDbsLatched - 1; i >= 0; i -= 1) {
3366                     DatabaseImpl dbImpl = dbImpls[i];
3367                     dbImpl.getTree().releaseRootLatch();
3368                 }
3369             }
3370         }
3371 
3372         /*
3373          * Method to get the Root IN for this DatabaseImpl's tree.
3374          */
3375         @Override
getRootIN(DatabaseImpl dbImpl, @SuppressWarnings(R) long rootLsn)3376         protected IN getRootIN(DatabaseImpl dbImpl,
3377                                @SuppressWarnings("unused") long rootLsn) {
3378             return dbImpl.getTree().getRootINRootAlreadyLatched(
3379                 CacheMode.UNCHANGED, false /*exclusive*/);
3380         }
3381 
3382         @Override
fetchAndInsertIntoTree()3383         protected boolean fetchAndInsertIntoTree() {
3384             return true;
3385         }
3386     }
3387 
getRecoveryProgressListener()3388     public ProgressListener<RecoveryProgress> getRecoveryProgressListener() {
3389         return recoveryProgressListener;
3390     }
3391 
getClassLoader()3392     public ClassLoader getClassLoader() {
3393         return classLoader;
3394     }
3395 
getDupConvertPreloadConfig()3396     public PreloadConfig getDupConvertPreloadConfig() {
3397         return dupConvertPreloadConfig;
3398     }
3399 }
3400