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 
8 package com.sleepycat.je.dbi;
9 
10 import static com.sleepycat.je.dbi.BTreeStatDefinition.BTREE_BINS_BYLEVEL;
11 import static com.sleepycat.je.dbi.BTreeStatDefinition.BTREE_BIN_COUNT;
12 import static com.sleepycat.je.dbi.BTreeStatDefinition.BTREE_DELETED_LN_COUNT;
13 import static com.sleepycat.je.dbi.BTreeStatDefinition.BTREE_INS_BYLEVEL;
14 import static com.sleepycat.je.dbi.BTreeStatDefinition.BTREE_IN_COUNT;
15 import static com.sleepycat.je.dbi.BTreeStatDefinition.BTREE_LN_COUNT;
16 import static com.sleepycat.je.dbi.BTreeStatDefinition.BTREE_MAINTREE_MAXDEPTH;
17 import static com.sleepycat.je.dbi.BTreeStatDefinition.BTREE_BIN_ENTRIES_HISTOGRAM;
18 import static com.sleepycat.je.dbi.BTreeStatDefinition.GROUP_DESC;
19 import static com.sleepycat.je.dbi.BTreeStatDefinition.GROUP_NAME;
20 
21 import java.io.ByteArrayInputStream;
22 import java.io.ByteArrayOutputStream;
23 import java.io.IOException;
24 import java.io.ObjectOutputStream;
25 import java.io.PrintStream;
26 import java.nio.ByteBuffer;
27 import java.util.Arrays;
28 import java.util.Collection;
29 import java.util.Collections;
30 import java.util.Comparator;
31 import java.util.HashSet;
32 import java.util.IdentityHashMap;
33 import java.util.Iterator;
34 import java.util.LinkedList;
35 import java.util.List;
36 import java.util.Map;
37 import java.util.Set;
38 import java.util.concurrent.atomic.AtomicInteger;
39 import java.util.concurrent.atomic.AtomicReference;
40 
41 import com.sleepycat.je.BinaryEqualityComparator;
42 import com.sleepycat.je.BtreeStats;
43 import com.sleepycat.je.CacheMode;
44 import com.sleepycat.je.CacheModeStrategy;
45 import com.sleepycat.je.Cursor;
46 import com.sleepycat.je.Database;
47 import com.sleepycat.je.DatabaseComparator;
48 import com.sleepycat.je.DatabaseConfig;
49 import com.sleepycat.je.DatabaseEntry;
50 import com.sleepycat.je.DatabaseException;
51 import com.sleepycat.je.DatabaseNotFoundException;
52 import com.sleepycat.je.DatabaseStats;
53 import com.sleepycat.je.DbInternal;
54 import com.sleepycat.je.EnvironmentFailureException;
55 import com.sleepycat.je.LockConflictException;
56 import com.sleepycat.je.LockMode;
57 import com.sleepycat.je.OperationStatus;
58 import com.sleepycat.je.PartialComparator;
59 import com.sleepycat.je.PreloadConfig;
60 import com.sleepycat.je.PreloadStats;
61 import com.sleepycat.je.SecondaryDatabase;
62 import com.sleepycat.je.StatsConfig;
63 import com.sleepycat.je.VerifyConfig;
64 import com.sleepycat.je.cleaner.BaseUtilizationTracker;
65 import com.sleepycat.je.cleaner.DbFileSummary;
66 import com.sleepycat.je.cleaner.DbFileSummaryMap;
67 import com.sleepycat.je.cleaner.LocalUtilizationTracker;
68 import com.sleepycat.je.config.EnvironmentParams;
69 import com.sleepycat.je.dbi.SortedLSNTreeWalker.TreeNodeProcessor;
70 import com.sleepycat.je.latch.LatchSupport;
71 import com.sleepycat.je.log.DbOpReplicationContext;
72 import com.sleepycat.je.log.LogEntryType;
73 import com.sleepycat.je.log.LogUtils;
74 import com.sleepycat.je.log.Loggable;
75 import com.sleepycat.je.log.ReplicationContext;
76 import com.sleepycat.je.log.entry.DbOperationType;
77 import com.sleepycat.je.tree.BIN;
78 import com.sleepycat.je.tree.IN;
79 import com.sleepycat.je.tree.Key;
80 import com.sleepycat.je.tree.LN;
81 import com.sleepycat.je.tree.Node;
82 import com.sleepycat.je.tree.Tree;
83 import com.sleepycat.je.tree.TreeUtils;
84 import com.sleepycat.je.tree.TreeWalkerStatsAccumulator;
85 import com.sleepycat.je.trigger.PersistentTrigger;
86 import com.sleepycat.je.trigger.Trigger;
87 import com.sleepycat.je.txn.BasicLocker;
88 import com.sleepycat.je.txn.LockType;
89 import com.sleepycat.je.txn.Locker;
90 import com.sleepycat.je.txn.LockerFactory;
91 import com.sleepycat.je.utilint.CmdUtil;
92 import com.sleepycat.je.utilint.DbLsn;
93 import com.sleepycat.je.utilint.IntStat;
94 import com.sleepycat.je.utilint.LongArrayStat;
95 import com.sleepycat.je.utilint.LongStat;
96 import com.sleepycat.je.utilint.Stat;
97 import com.sleepycat.je.utilint.StatGroup;
98 import com.sleepycat.je.utilint.TestHook;
99 import com.sleepycat.je.utilint.TestHookExecute;
100 import com.sleepycat.util.ClassResolver;
101 
102 /**
103  * The underlying object for a given database.
104  */
105 public class DatabaseImpl implements Loggable, Cloneable {
106 
107     /*
108      * Delete processing states. See design note on database deletion and
109      * truncation
110      */
111     private static final short NOT_DELETED = 1;
112     private static final short DELETED_CLEANUP_INLIST_HARVEST = 2;
113     private static final short DELETED_CLEANUP_LOG_HARVEST = 3;
114     private static final short DELETED = 4;
115 
116     /*
117      * Flag bits are the persistent representation of boolean properties
118      * for this database.  The DUPS_ENABLED value is 1 for compatibility
119      * with earlier log entry versions where it was stored as a boolean.
120      *
121      * Two bits are used to indicate whether this database is replicated or
122      * not.
123      * isReplicated = 0, notReplicated = 0 means replication status is
124      *   unknown, because the db was created in an standalone environment.
125      * isReplicated = 1, notReplicated = 0 means the db is replicated.
126      * isReplicated = 0, notReplicated = 1 means the db is not replicated.
127      * isReplicated = 1, notReplicated = 1 is an illegal combination.
128      */
129     private byte flags;
130     private static final byte DUPS_ENABLED = 0x1;      // getSortedDuplicates()
131     private static final byte TEMPORARY_BIT = 0x2;     // isTemporary()
132     private static final byte IS_REPLICATED_BIT = 0x4; // isReplicated()
133     private static final byte NOT_REPLICATED_BIT = 0x8;// notReplicated()
134     private static final byte PREFIXING_ENABLED = 0x10;// getKeyPrefixing()
135     private static final byte UTILIZATION_REPAIR_DONE = 0x20;
136                                                   // getUtilizationRepairDone()
137     private static final byte DUPS_CONVERTED = 0x40;   // getKeyPrefixing()
138 
139     private DatabaseId id;             // unique id
140     private Tree tree;
141     private EnvironmentImpl envImpl;   // Tree operations find the env this way
142     private boolean transactional;     // All open handles are transactional
143     private boolean durableDeferredWrite;  // Durable deferred write mode set
144     private volatile boolean dirty;    // Utilization, root LSN, etc., changed
145     private Set<Database> referringHandles; // Set of open Database handles
146     private BtreeStats stats;     // most recent btree stats w/ !DB_FAST_STAT
147     private long eofLsn;          // Logical EOF LSN for range locking
148     private volatile short deleteState;    // one of four delete states.
149     private AtomicInteger useCount = new AtomicInteger();
150                                   // If non-zero, eviction is prohibited
151     /*
152      * Tracks the number of write handle references to this impl. It's used
153      * to determine the when the Trigger.open/close methods must be invoked.
154      */
155     private final AtomicInteger writeCount = new AtomicInteger();
156 
157     private DbFileSummaryMap dbFileSummaries;
158 
159     /**
160      * Log version when DB was created, or 0 if created prior to log version 6.
161      */
162     private byte createdAtLogVersion;
163 
164     /**
165      * For unit testing, setting this field to true will force a walk of the
166      * tree to count utilization during truncate/remove, rather than using the
167      * per-database info.  This is used to test the "old technique" for
168      * counting utilization, which is now used only if the database was created
169      * prior to log version 6.
170      */
171     public static boolean forceTreeWalkForTruncateAndRemove;
172 
173     /*
174      * The user defined Btree and duplicate comparison functions, if specified.
175      */
176     private Comparator<byte[]> btreeComparator = null;
177     private Comparator<byte[]> duplicateComparator = null;
178     private byte[] btreeComparatorBytes = LogUtils.ZERO_LENGTH_BYTE_ARRAY;
179     private byte[] duplicateComparatorBytes = LogUtils.ZERO_LENGTH_BYTE_ARRAY;
180 
181     private boolean btreeComparatorByClassName = false;
182     private boolean duplicateComparatorByClassName = false;
183     private boolean btreePartialComparator = false;
184     private boolean duplicatePartialComparator = false;
185     private boolean btreeBinaryEqualityComparator = true;
186     private boolean duplicateBinaryEqualityComparator = true;
187 
188     /* Key comparator uses the btree and dup comparators as needed. */
189     private Comparator<byte[]> keyComparator = null;
190 
191     /*
192      * The user defined triggers associated with this database.
193      *
194      * The triggers reference value contains all known triggers, persistent and
195      * transient, or null if it has not yet been constructed, which is done
196      * lazily.  It is constructed by unmarshalling the triggerBytes (persistent
197      * triggers) and adding them to the transientTriggers.
198      *
199      * transientTriggers is null if there are none, and never an empty list.
200      */
201     private AtomicReference<List<Trigger>> triggers =
202         new AtomicReference<List<Trigger>>(null);
203     private List<Trigger> transientTriggers = null;
204     private byte[][] triggerBytes = null;
205 
206     /*
207      * Cache some configuration values.
208      */
209     private int binDeltaPercent;
210     private int maxTreeEntriesPerNode;
211 
212     private String debugDatabaseName;
213 
214     /* For unit tests */
215     private TestHook<?> pendingDeletedHook;
216 
217     /*
218      * The DbType of this DatabaseImpl.  Is determined lazily, so getDbType
219      * should always be called rather than referencing the field directly.
220      */
221     private DbType dbType;
222 
223     private CacheMode cacheMode;
224     private CacheModeStrategy cacheModeStrategy;
225 
226     /*
227      * For debugging -- this gives the ability to force all non-internal
228      * databases to use key prefixing.
229      *
230      * Note that doing
231      *     ant -Dje.forceKeyPrefixing=true test
232      * does not work because ant does not pass the parameter down to JE.
233      */
234     private static final boolean forceKeyPrefixing;
235     static {
236         String forceKeyPrefixingProp =
237             System.getProperty("je.forceKeyPrefixing");
238         if ("true".equals(forceKeyPrefixingProp)) {
239             forceKeyPrefixing = true;
240         } else {
241             forceKeyPrefixing = false;
242         }
243     }
244 
245     /**
246      * Create a database object for a new database.
247      */
DatabaseImpl(Locker locker, String dbName, DatabaseId id, EnvironmentImpl envImpl, DatabaseConfig dbConfig)248     public DatabaseImpl(Locker locker,
249                         String dbName,
250                         DatabaseId id,
251                         EnvironmentImpl envImpl,
252                         DatabaseConfig dbConfig)
253         throws DatabaseException {
254 
255         this.id = id;
256         this.envImpl = envImpl;
257 
258         setConfigProperties(locker, dbName, dbConfig, envImpl);
259         cacheMode = dbConfig.getCacheMode();
260         cacheModeStrategy = dbConfig.getCacheModeStrategy();
261 
262         createdAtLogVersion = LogEntryType.LOG_VERSION;
263 
264         /* A new DB is implicitly converted to the new dups format. */
265         if (getSortedDuplicates()) {
266             setDupsConverted();
267         }
268 
269         /*
270          * New DB records do not need utilization repair.  Set this before
271          * calling initWithEnvironment to avoid repair overhead.
272          */
273         setUtilizationRepairDone();
274 
275         commonInit();
276 
277         initWithEnvironment();
278 
279         /*
280          * The tree needs the env, make sure we assign it before
281          * allocating the tree.
282          */
283         tree = new Tree(this);
284 
285         /* For error messages only. */
286         debugDatabaseName = dbName;
287     }
288 
289     /**
290      * Create an empty database object for initialization from the log.  Note
291      * that the rest of the initialization comes from readFromLog(), except
292      * for the debugDatabaseName, which is set by the caller.
293      */
DatabaseImpl()294     public DatabaseImpl() {
295         id = new DatabaseId();
296         envImpl = null;
297 
298         tree = new Tree();
299 
300         commonInit();
301 
302         /* initWithEnvironment is called after reading and envImpl is set.  */
303     }
304 
305     /* Set the DatabaseConfig properties for a DatabaseImpl. */
setConfigProperties(Locker locker, String dbName, DatabaseConfig dbConfig, EnvironmentImpl envImpl)306     public void setConfigProperties(Locker locker,
307                                     String dbName,
308                                     DatabaseConfig dbConfig,
309                                     EnvironmentImpl envImpl) {
310         setBtreeComparator(dbConfig.getBtreeComparator(),
311                            dbConfig.getBtreeComparatorByClassName());
312         setDuplicateComparator(dbConfig.getDuplicateComparator(),
313                                dbConfig.getDuplicateComparatorByClassName());
314 
315         setTriggers(locker, dbName, dbConfig.getTriggers(),
316                     true /*overridePersistentTriggers*/);
317 
318         if (dbConfig.getSortedDuplicates()) {
319             setSortedDuplicates();
320         }
321 
322         if (dbConfig.getKeyPrefixing() ||
323             forceKeyPrefixing) {
324             setKeyPrefixing();
325         } else {
326             clearKeyPrefixing();
327         }
328 
329         if (dbConfig.getTemporary()) {
330             setTemporary();
331         }
332 
333         if (envImpl.isReplicated()) {
334             if (dbConfig.getReplicated()) {
335                 setIsReplicatedBit();
336             } else {
337                 setNotReplicatedBit();
338             }
339         }
340 
341         transactional = dbConfig.getTransactional();
342         durableDeferredWrite = dbConfig.getDeferredWrite();
343         maxTreeEntriesPerNode = dbConfig.getNodeMaxEntries();
344     }
345 
commonInit()346     private void commonInit() {
347 
348         deleteState = NOT_DELETED;
349         referringHandles =
350             Collections.synchronizedSet(new HashSet<Database>());
351         dbFileSummaries = new DbFileSummaryMap
352             (false /* countParentMapEntry */);
353     }
354 
setDebugDatabaseName(String debugName)355     public void setDebugDatabaseName(String debugName) {
356         debugDatabaseName = debugName;
357             /* DbType may be wrong if name has not yet been set. */
358         resetDbType();
359     }
360 
361     /**
362      * Returns the DB name for debugging and error messages.  This method
363      * should be called rather than getName to avoid accessing the db mapping
364      * tree in error situations  The name may not be transactionally correct,
365      * and may be unknown under certain circumstances (see
366      * DbTree.setDebugNameForDatabaseImpl) in which case a string containing
367      * the DB ID is returned.
368      */
getDebugName()369     public String getDebugName() {
370         return (debugDatabaseName != null) ? debugDatabaseName : "dBId=" + id;
371     }
372 
373     /**
374      * Returns whether getDebugName returns a DB name rather than a DB ID.
375      */
isDebugNameAvailable()376     boolean isDebugNameAvailable() {
377         return (debugDatabaseName != null);
378     }
379 
380     /* For unit testing only. */
setPendingDeletedHook(TestHook<?> hook)381     public void setPendingDeletedHook(TestHook<?> hook) {
382         pendingDeletedHook = hook;
383     }
384 
385     /**
386      * Initialize configuration settings when creating a new instance or after
387      * reading an instance from the log.  The envImpl field must be set before
388      * calling this method.
389      */
initWithEnvironment()390     private void initWithEnvironment() {
391         /* The eof LSN must be unique for each database in memory. */
392         eofLsn = envImpl.getNodeSequence().getNextTransientLsn();
393 
394         assert !(replicatedBitSet() && notReplicatedBitSet()) :
395             "The replicated AND notReplicated bits should never be set "+
396             " together";
397 
398         /*
399          * We'd like to assert that neither replication bit is set if
400          * the environmentImpl is not replicated, but can't do that.
401          * EnvironmentImpl.isReplicated() is not yet initialized if this
402          * environment is undergoing recovery during replication setup.
403 
404         assert !((!envImpl.isReplicated() &&
405                  (replicatedBitSet() || notReplicatedBitSet()))) :
406             "Neither the replicated nor notReplicated bits should be set " +
407             " in a non-replicated environment" +
408             " replicatedBitSet=" + replicatedBitSet() +
409             " notRepBitSet=" + notReplicatedBitSet();
410         */
411 
412         DbConfigManager configMgr = envImpl.getConfigManager();
413 
414         binDeltaPercent =
415             configMgr.getInt(EnvironmentParams.BIN_DELTA_PERCENT);
416 
417         /*
418          * If maxTreeEntriesPerNode is zero (for a newly created database),
419          * set it to the default config value.  When we write the DatabaseImpl
420          * to the log, we'll store the default.  That way, if the default
421          * changes, the fan out for existing databases won't change.
422          */
423         if (maxTreeEntriesPerNode == 0) {
424             maxTreeEntriesPerNode =
425                 configMgr.getInt(EnvironmentParams.NODE_MAX);
426         }
427 
428         /* Budgets memory for the utilization info. */
429         dbFileSummaries.init(envImpl);
430 
431         /*
432          * Repair utilization info if necessary.  The repair flag will not be
433          * set for MapLNs written by JE 3.3.74 and earlier, and will be set for
434          * all MapLNs written thereafter.  Make the utilization dirty to force
435          * the MapLN to be flushed.  Even if no repair is performed, we want to
436          * write the updated flag.  [#16610]
437          */
438         if (!getUtilizationRepairDone()) {
439             dbFileSummaries.repair(envImpl);
440             setDirty();
441             setUtilizationRepairDone();
442         }
443 
444         /* Don't instantiate if comparators are unnecessary (DbPrintLog). */
445         if (!envImpl.getNoComparators()) {
446 
447             ComparatorReader reader = new ComparatorReader(
448                 btreeComparatorBytes, "Btree", envImpl.getClassLoader());
449             btreeComparator = reader.getComparator();
450             btreeComparatorByClassName = reader.isClass();
451             btreePartialComparator =
452                 btreeComparator instanceof PartialComparator;
453             btreeBinaryEqualityComparator =
454                 (btreeComparator == null ||
455                  btreeComparator instanceof BinaryEqualityComparator);
456 
457             reader = new ComparatorReader(
458                 duplicateComparatorBytes, "Duplicate",
459                 envImpl.getClassLoader());
460             duplicateComparator = reader.getComparator();
461             duplicateComparatorByClassName = reader.isClass();
462             duplicatePartialComparator =
463                 duplicateComparator instanceof PartialComparator;
464             duplicateBinaryEqualityComparator =
465                 (duplicateComparator == null ||
466                  duplicateComparator instanceof BinaryEqualityComparator);
467 
468             /* Key comparator is derived from dup and btree comparators. */
469             resetKeyComparator();
470         }
471     }
472 
473     /**
474      * Create a clone of this database that can be used as the new empty
475      * database when truncating this database.  setId and setTree must be
476      * called on the returned database.
477      */
cloneDatabase()478     public DatabaseImpl cloneDatabase() {
479         DatabaseImpl newDb;
480         try {
481             newDb = (DatabaseImpl) super.clone();
482         } catch (CloneNotSupportedException e) {
483             assert false : e;
484             return null;
485         }
486 
487         /* Re-initialize fields that should not be shared by the new DB. */
488         newDb.id = null;
489         newDb.tree = null;
490         newDb.createdAtLogVersion = LogEntryType.LOG_VERSION;
491         newDb.dbFileSummaries = new DbFileSummaryMap
492             (false /*countParentMapEntry*/);
493         newDb.dbFileSummaries.init(envImpl);
494         newDb.useCount = new AtomicInteger();
495         return newDb;
496     }
497 
498     /**
499      * @return the database tree.
500      */
getTree()501     public Tree getTree() {
502         return tree;
503     }
504 
setTree(Tree tree)505     void setTree(Tree tree) {
506         this.tree = tree;
507     }
508 
509     /**
510      * @return the database id.
511      */
getId()512     public DatabaseId getId() {
513         return id;
514     }
515 
setId(DatabaseId id)516     void setId(DatabaseId id) {
517         this.id = id;
518     }
519 
getEofLsn()520     public long getEofLsn() {
521         return eofLsn;
522     }
523 
524     /**
525      * @return true if this database is transactional.
526      */
isTransactional()527     public boolean isTransactional() {
528         return transactional;
529     }
530 
531     /**
532      * Sets the transactional property for the first opened handle.
533      */
setTransactional(boolean transactional)534     public void setTransactional(boolean transactional) {
535         this.transactional = transactional;
536     }
537 
538     /**
539      * @return true if this database is temporary.
540      */
isTemporary()541     public boolean isTemporary() {
542         return ((flags & TEMPORARY_BIT) != 0);
543     }
544 
isTemporary(byte flagVal)545     public static boolean isTemporary(byte flagVal) {
546         return ((flagVal & TEMPORARY_BIT) != 0);
547     }
548 
isInternalDb()549     public boolean isInternalDb() {
550         return getDbType().isInternal();
551     }
552 
getDbType()553     public DbType getDbType() {
554         if (dbType != null) {
555             return dbType;
556         }
557         resetDbType();
558         return dbType;
559     }
560 
resetDbType()561     private void resetDbType() {
562         dbType = DbTree.typeForDbName(debugDatabaseName);
563     }
564 
setTemporary()565     private void setTemporary() {
566         flags |= TEMPORARY_BIT;
567     }
568 
569     /**
570      * @return true if this database was user configured for durable deferred
571      * write mode.
572      */
isDurableDeferredWrite()573     public boolean isDurableDeferredWrite() {
574         return durableDeferredWrite;
575     }
576 
577     /**
578      * @return true if write operations are not logged immediately.  This is
579      * true if the user configured a durable DW database or a temporary
580      * database.
581      */
isDeferredWriteMode()582     public boolean isDeferredWriteMode() {
583         return isDurableDeferredWrite() || isTemporary();
584     }
585 
586     /**
587      * Sets the deferred write property for the first opened handle.
588      */
setDeferredWrite(boolean durableDeferredWrite)589     public void setDeferredWrite(boolean durableDeferredWrite) {
590         this.durableDeferredWrite = durableDeferredWrite;
591     }
592 
593     /**
594      * @return true if duplicates are allowed in this database.
595      */
getSortedDuplicates()596     public boolean getSortedDuplicates() {
597         return (flags & DUPS_ENABLED) != 0;
598     }
599 
getSortedDuplicates(byte flagVal)600     public static boolean getSortedDuplicates(byte flagVal) {
601         return (flagVal & DUPS_ENABLED) != 0;
602     }
603 
setSortedDuplicates()604     public void setSortedDuplicates() {
605         flags |= DUPS_ENABLED;
606     }
607 
getDupsConverted()608     public boolean getDupsConverted() {
609         return (flags & DUPS_CONVERTED) != 0;
610     }
611 
setDupsConverted()612     public void setDupsConverted() {
613         flags |= DUPS_CONVERTED;
614     }
615 
616     /**
617      * Returns whether all LNs in this DB are "immediately obsolete", meaning
618      * two things:
619      * 1) They are counted obsolete when logged and can be ignored by the
620      *    cleaner entirely.
621      * 2) As a consequence, they cannot be fetched by LSN, except under special
622      *    circumstances where they are known to exist.
623      *
624      * Currently, this is synonymous with whether all LNs in this DB must have
625      * zero length data, and partial comparators are not used.  Currently only
626      * duplicate DBs are known to have zero length LNs, since there is no way
627      * in the API to specify that LNs are immutable.  In the future we will
628      * also support "immediately obsolete" LNs that are mutable and embedded
629      * in the BIN in other ways, e.g., tiny data may be stored with the key.
630      *
631      * Note that deleted LNs (the logged deletion, not the prior version) are
632      * always immediately obsolete also.  See LNLogEntry.isImmediatelyObsolete.
633      */
isLNImmediatelyObsolete()634     public boolean isLNImmediatelyObsolete() {
635         return getSortedDuplicates() &&
636             !btreePartialComparator &&
637             !duplicatePartialComparator;
638     }
639 
640     /**
641      * This method should be the only method used to obtain triggers after
642      * reading the MapLN from the log.  It unmarshalls the triggers lazily
643      * here to avoid a call to getName() during recovery, when the DbTree is
644      * not yet instantiated.
645      */
getTriggers()646     public List<Trigger> getTriggers() {
647 
648         /* When comparators are not needed, neither are triggers. */
649         if (envImpl == null || envImpl.getNoComparators()) {
650             return null;
651         }
652 
653         /* If no transient or persistent triggers, return null. */
654         if (triggerBytes == null && transientTriggers == null) {
655             return null;
656         }
657 
658         /* Just return them, if already constructed. */
659         List<Trigger> myTriggers = triggers.get();
660         if (myTriggers != null) {
661             return myTriggers;
662         }
663 
664         /*
665          * Unmarshall triggers, add transient triggers, and update the
666          * reference atomically. If another thread unmarshalls and updates it
667          * first, use the value set by the other thread.  This ensures that a
668          * single instance is always used.
669          */
670         myTriggers = TriggerUtils.unmarshallTriggers(getName(), triggerBytes,
671                                                      envImpl.getClassLoader());
672         if (myTriggers == null) {
673             myTriggers = new LinkedList<Trigger>();
674         }
675         if (transientTriggers != null) {
676             myTriggers.addAll(transientTriggers);
677         }
678         if (triggers.compareAndSet(null, myTriggers)) {
679             return myTriggers;
680         }
681         myTriggers = triggers.get();
682         assert myTriggers != null;
683         return myTriggers;
684     }
685 
hasUserTriggers()686     public boolean hasUserTriggers() {
687         return (triggerBytes != null) || (transientTriggers != null);
688     }
689 
690     /**
691      * @return true if key prefixing is enabled in this database.
692      */
getKeyPrefixing()693     public boolean getKeyPrefixing() {
694         return (flags & PREFIXING_ENABLED) != 0;
695     }
696 
697     /**
698      * Returns true if the flagVal enables the KeyPrefixing, used to create
699      * ReplicatedDatabaseConfig after reading a NameLNLogEntry.
700      */
getKeyPrefixing(byte flagVal)701     public static boolean getKeyPrefixing(byte flagVal) {
702         return (flagVal & PREFIXING_ENABLED) != 0;
703     }
704 
setKeyPrefixing()705     public void setKeyPrefixing() {
706         flags |= PREFIXING_ENABLED;
707     }
708 
clearKeyPrefixing()709     public void clearKeyPrefixing() {
710         if (forceKeyPrefixing) {
711             return;
712         }
713         flags &= ~PREFIXING_ENABLED;
714     }
715 
716     /**
717      * @return true if this database is replicated. Note that we only need to
718      * check the IS_REPLICATED_BIT, because we require that we never have both
719      * IS_REPLICATED and NOT_REPLICATED set at the same time.
720      */
isReplicated()721     public boolean isReplicated() {
722         return replicatedBitSet();
723     }
724 
725     /**
726      * @return true if this database is replicated.
727      */
unknownReplicated()728     public boolean unknownReplicated() {
729         return ((flags & IS_REPLICATED_BIT) == 0) &&
730             ((flags & NOT_REPLICATED_BIT) == 0);
731     }
732 
replicatedBitSet()733     private boolean replicatedBitSet() {
734         return (flags & IS_REPLICATED_BIT) != 0;
735     }
736 
setIsReplicatedBit()737     public void setIsReplicatedBit() {
738         flags |= IS_REPLICATED_BIT;
739     }
740 
741     /**
742      * @return true if this database's not replicated bit is set.
743      */
notReplicatedBitSet()744     private boolean notReplicatedBitSet() {
745         return (flags & NOT_REPLICATED_BIT) != 0;
746     }
747 
setNotReplicatedBit()748     private void setNotReplicatedBit() {
749         flags |= NOT_REPLICATED_BIT;
750     }
751 
752     /**
753      * Is public for unit testing.
754      */
getUtilizationRepairDone()755     public boolean getUtilizationRepairDone() {
756         return (flags & UTILIZATION_REPAIR_DONE) != 0;
757     }
758 
setUtilizationRepairDone()759     private void setUtilizationRepairDone() {
760         flags |= UTILIZATION_REPAIR_DONE;
761     }
762 
763     /**
764      * Is public for unit testing.
765      */
clearUtilizationRepairDone()766     public void clearUtilizationRepairDone() {
767         flags &= ~UTILIZATION_REPAIR_DONE;
768     }
769 
getNodeMaxTreeEntries()770     public int getNodeMaxTreeEntries() {
771         return maxTreeEntriesPerNode;
772     }
773 
setNodeMaxTreeEntries(int newNodeMaxTreeEntries)774     public void setNodeMaxTreeEntries(int newNodeMaxTreeEntries) {
775         maxTreeEntriesPerNode = newNodeMaxTreeEntries;
776     }
777 
778     /**
779      * Used to determine whether to throw ReplicaWriteException when a write to
780      * this database is attempted.  For the most part, writes on a replica are
781      * not allowed to any replicated DB.  However, an exception is the DB
782      * naming DB.  The naming DB contains a mixture of LNs for replicated and
783      * non-replicated databases.  Here, we allow all writes to the naming DB.
784      * DB naming operations for replicated databases on a replica, such as the
785      * creation of a replicated DB on a replica, are prohibited by DbTree
786      * methods (dbCreate, dbRemove, etc). [#20543]
787      */
allowReplicaWrite()788     public boolean allowReplicaWrite() {
789         return !isReplicated() || getDbType() == DbType.NAME;
790     }
791 
792     /**
793      * Sets the default mode for this database (all handles).  May be null to
794      * use Environment default.
795      */
setCacheMode(CacheMode mode)796     public void setCacheMode(CacheMode mode) {
797         cacheMode = mode;
798     }
799 
800     /**
801      * Sets the default strategy for this database (all handles).  May be null
802      * to use Environment default.
803      */
setCacheModeStrategy(CacheModeStrategy strategy)804     public void setCacheModeStrategy(CacheModeStrategy strategy) {
805         cacheModeStrategy = strategy;
806     }
807 
808     /**
809      * Returns the default cache mode for this database. If the database has a
810      * null cache mode and is not an internal database, the Environment default
811      * is returned.  Null is never returned. CacheMode.DYNAMIC may be returned.
812      */
getDefaultCacheMode()813     public CacheMode getDefaultCacheMode() {
814         if (cacheMode != null) {
815             return cacheMode;
816         }
817         if (isInternalDb()) {
818             return CacheMode.DEFAULT;
819         }
820         return envImpl.getDefaultCacheMode();
821     }
822 
823     /**
824      * Returns the effective cache mode to use for a cursor operation, based on
825      * the given non-null cache mode parameter.  If the cache mode parameter is
826      * CacheMode.DYNAMIC, then the CacheModeStrategy is applied and therefore
827      * CacheMode.DYNAMIC itself is never returned.  Null is never returned.
828      */
getEffectiveCacheMode(final CacheMode cacheModeParam)829     public CacheMode getEffectiveCacheMode(final CacheMode cacheModeParam) {
830         assert cacheModeParam != null;
831         if (cacheModeParam != CacheMode.DYNAMIC) {
832             return cacheModeParam;
833         }
834         final CacheModeStrategy strategy = (cacheModeStrategy != null) ?
835             cacheModeStrategy :
836             envImpl.getDefaultCacheModeStrategy();
837         if (strategy == null) {
838             throw new IllegalStateException
839                 ("CacheMode.DYNAMIC may not be used without also configuring" +
840                  " a CacheModeStrategy for the Database or Environment.");
841         }
842         final CacheMode dynamicMode = strategy.getCacheMode();
843         if (dynamicMode == null || dynamicMode == CacheMode.DYNAMIC) {
844             throw new IllegalArgumentException
845                 ("" + dynamicMode + " was illegally returned by " +
846                  strategy.getClass().getName());
847         }
848         return dynamicMode;
849     }
850 
851     /**
852      * Returns the tree memory size that should be added to MAPLN_OVERHEAD.
853      *
854      * This is a start at budgeting per-Database memory.  For future reference,
855      * other things that could be budgeted are:
856      * - debugDatabaseName as it is set
857      * - Database handles as they are added/removed in referringHandles
858      */
getAdditionalTreeMemorySize()859     public int getAdditionalTreeMemorySize() {
860 
861         int val = 0;
862 
863         /*
864          * If the comparator object is non-null we double the size of the
865          * serialized form to account for the approximate size of the user's
866          * comparator object.  This is only an approximation of course, and is
867          * not a very good one if we have serialized the class name, but we
868          * have no way to know the size of the user's object.
869          */
870         if (btreeComparator != null) {
871             val += 2 * MemoryBudget.byteArraySize
872                 (btreeComparatorBytes.length);
873         }
874         if (duplicateComparator != null) {
875             val += 2 * MemoryBudget.byteArraySize
876                 (duplicateComparatorBytes.length);
877         }
878 
879         return val;
880     }
881 
882     /**
883      * Set the duplicate comparison function for this database.
884      *
885      * @return true if the comparator was actually changed
886      *
887      * @param comparator - The Duplicate Comparison function.
888      */
setDuplicateComparator( Comparator<byte[]> comparator, boolean byClassName)889     public boolean setDuplicateComparator(
890         Comparator<byte[]> comparator,
891         boolean byClassName)
892         throws DatabaseException {
893 
894         final byte[] newBytes =
895             comparatorToBytes(comparator, byClassName, "Duplicate");
896 
897         final boolean changed =
898             !Arrays.equals(newBytes, duplicateComparatorBytes) ||
899             ((comparator instanceof PartialComparator) !=
900              (duplicateComparator instanceof PartialComparator)) ||
901             ((comparator instanceof BinaryEqualityComparator) !=
902              (duplicateComparator instanceof BinaryEqualityComparator));
903 
904         duplicateComparator = comparator;
905         duplicateComparatorBytes = newBytes;
906         duplicateComparatorByClassName = byClassName;
907 
908         duplicatePartialComparator =
909             duplicateComparator instanceof PartialComparator;
910 
911         duplicateBinaryEqualityComparator =
912             (duplicateComparator == null ||
913              duplicateComparator instanceof BinaryEqualityComparator);
914 
915         if (changed) {
916             /* Key comparator is derived from dup and btree comparators. */
917             resetKeyComparator();
918         }
919         return changed;
920     }
921 
922     /**
923      * Sets the list of triggers associated with the database.
924      *
925      * @param dbName pass it in since it may not be available during database
926      * creation
927      * @param newTriggers the triggers to associate with the database
928      * @param overridePersistentTriggers whether to overwrite persistent
929      * triggers
930      *
931      * @return true if a {@link PersistentTrigger} was changed, and therefore
932      * may need to be stored.
933      */
setTriggers(Locker locker, String dbName, List<Trigger> newTriggers, boolean overridePersistentTriggers)934     public boolean setTriggers(Locker locker,
935                                String dbName,
936                                List<Trigger> newTriggers,
937                                boolean overridePersistentTriggers) {
938 
939         if ((newTriggers != null) && (newTriggers.size() == 0)) {
940             newTriggers = null;
941         }
942 
943         /* Construct new persistent triggers. */
944         final byte newTriggerBytes[][];
945         final boolean persistentChange;
946 
947         if (overridePersistentTriggers) {
948             if (newTriggers == null) {
949                 newTriggerBytes = null;
950                 persistentChange = (this.triggerBytes != null);
951             } else {
952                 /* Create the new trigger bytes. */
953                 int nTriggers = 0;
954                 for (Trigger trigger : newTriggers) {
955                     if (trigger instanceof PersistentTrigger) {
956                         nTriggers += 1;
957                     }
958                 }
959                 if (nTriggers == 0) {
960                     newTriggerBytes = null;
961                     persistentChange = (this.triggerBytes != null);
962                 } else {
963                     newTriggerBytes = new byte[nTriggers][];
964                     int i=0;
965                     for (Trigger trigger : newTriggers) {
966                         if (trigger instanceof PersistentTrigger) {
967                             newTriggerBytes[i++] = objectToBytes
968                                 (trigger, "trigger " + trigger.getName());
969                             trigger.setDatabaseName(dbName);
970                         }
971                     }
972                     persistentChange =
973                         !Arrays.equals(triggerBytes, newTriggerBytes);
974                 }
975             }
976         } else {
977             newTriggerBytes = triggerBytes;
978             persistentChange = false;
979         }
980 
981         /* Add transient triggers. */
982         final List<Trigger> newTransientTriggers;
983         final boolean transientChange;
984 
985         if (newTriggers == null) {
986             newTransientTriggers = null;
987             transientChange = (transientTriggers != null);
988         } else {
989             newTransientTriggers = new LinkedList<Trigger>();
990             final Map<Trigger, Object> diffs =
991                 new IdentityHashMap<Trigger, Object>();
992             for (Trigger trigger : newTriggers) {
993                 if (!(trigger instanceof PersistentTrigger)) {
994                     diffs.put(trigger, null);
995                     newTransientTriggers.add(trigger);
996                     trigger.setDatabaseName(dbName);
997                 }
998             }
999             if (transientTriggers == null) {
1000                 transientChange = (newTransientTriggers.size() > 0);
1001             } else if (transientTriggers.size() !=
1002                        newTransientTriggers.size()) {
1003                 transientChange = true;
1004             } else {
1005                 for (Trigger trigger : transientTriggers) {
1006                     diffs.remove(trigger);
1007                 }
1008                 transientChange = (diffs.size() > 0);
1009             }
1010         }
1011 
1012         if (persistentChange || transientChange) {
1013             TriggerManager.invokeAddRemoveTriggers(locker,
1014                                                    getTriggers(),
1015                                                    newTriggers);
1016             /* Don't change fields until after getTriggers() call above. */
1017             triggerBytes = newTriggerBytes;
1018             transientTriggers =
1019                 ((newTransientTriggers != null) &&
1020                  (newTransientTriggers.size() > 0)) ?
1021                 newTransientTriggers :
1022                 null;
1023             this.triggers.set(newTriggers);
1024         }
1025 
1026         return persistentChange;
1027     }
1028 
1029     /**
1030      * Called when a database is closed to clear all transient triggers and
1031      * call their 'removeTrigger' methods.
1032      */
clearTransientTriggers()1033     private void clearTransientTriggers() {
1034         final List<Trigger> oldTriggers = getTriggers();
1035         if (oldTriggers == null) {
1036             return;
1037         }
1038         final List<Trigger> newTriggers = new LinkedList<Trigger>(oldTriggers);
1039         final Iterator<Trigger> iter = newTriggers.iterator();
1040         while (iter.hasNext()) {
1041             final Trigger trigger = iter.next();
1042             if (!(trigger instanceof PersistentTrigger)) {
1043                 iter.remove();
1044             }
1045         }
1046         /* The dbName param can be null because it is not used. */
1047         setTriggers(null /*locker*/, null /*dbName*/, newTriggers,
1048                     false /*overridePersistentTriggers*/);
1049     }
1050 
1051     /**
1052      * Set the btree comparison function for this database.
1053      *
1054      * @return true if the comparator was actually changed
1055      *
1056      * @param comparator - The btree Comparison function.
1057      */
setBtreeComparator( Comparator<byte[]> comparator, boolean byClassName)1058     public boolean setBtreeComparator(
1059         Comparator<byte[]> comparator,
1060         boolean byClassName)
1061         throws DatabaseException {
1062 
1063         final byte[] newBytes =
1064             comparatorToBytes(comparator, byClassName, "Btree");
1065 
1066         final boolean changed =
1067             !Arrays.equals(newBytes, btreeComparatorBytes) ||
1068             ((btreeComparator instanceof PartialComparator) !=
1069              (comparator instanceof PartialComparator)) ||
1070             ((btreeComparator instanceof BinaryEqualityComparator) !=
1071              (comparator instanceof BinaryEqualityComparator));
1072 
1073         btreeComparator = comparator;
1074         btreeComparatorBytes = newBytes;
1075         btreeComparatorByClassName = byClassName;
1076 
1077         btreePartialComparator =
1078             btreeComparator instanceof PartialComparator;
1079 
1080         btreeBinaryEqualityComparator =
1081             (btreeComparator == null ||
1082              btreeComparator instanceof BinaryEqualityComparator);
1083 
1084         if (changed) {
1085             /* Key comparator is derived from dup and btree comparators. */
1086             resetKeyComparator();
1087         }
1088         return changed;
1089     }
1090 
1091     /**
1092      * This comparator should not be used directly for comparisons.  Use
1093      * getKeyComparator instead.
1094      *
1095      * @return the btree Comparator object.
1096      */
getBtreeComparator()1097     public Comparator<byte[]> getBtreeComparator() {
1098         return btreeComparator;
1099     }
1100 
1101     /**
1102      * This comparator should not be used directly for comparisons.  Use
1103      * getKeyComparator instead.
1104      *
1105      * @return the duplicate Comparator object.
1106      */
getDuplicateComparator()1107     public Comparator<byte[]> getDuplicateComparator() {
1108         return duplicateComparator;
1109     }
1110 
1111     /**
1112      * Key comparator is derived from the duplicate and btree comparator
1113      */
resetKeyComparator()1114     private void resetKeyComparator() {
1115 
1116         /* Initialize comparators. */
1117         if (btreeComparator instanceof DatabaseComparator) {
1118             ((DatabaseComparator) btreeComparator).initialize
1119                 (envImpl.getClassLoader());
1120         }
1121         if (duplicateComparator instanceof DatabaseComparator) {
1122             ((DatabaseComparator) duplicateComparator).initialize
1123                 (envImpl.getClassLoader());
1124         }
1125 
1126         /* Create derived comparator for duplicate database. */
1127         if (getSortedDuplicates()) {
1128             keyComparator = new DupKeyData.TwoPartKeyComparator
1129                 (btreeComparator, duplicateComparator);
1130         } else {
1131             keyComparator = btreeComparator;
1132         }
1133     }
1134 
1135     /**
1136      * Should always be used when comparing keys for this database.
1137      *
1138      * For a duplicates database, the data is part two of the two-part database
1139      * key.  Therefore, the duplicates comparator and btree comparator are used
1140      * for comparing keys.  This synthetic comparator will call both of the
1141      * other two user-defined comparators as necessary.
1142      */
getKeyComparator()1143     public Comparator<byte[]> getKeyComparator() {
1144         return keyComparator;
1145     }
1146 
1147     /**
1148      * @return whether Comparator is set by class name, not by serializable
1149      * Comparator object.
1150      */
getBtreeComparatorByClass()1151     public boolean getBtreeComparatorByClass() {
1152         return btreeComparatorByClassName;
1153     }
1154 
1155     /**
1156      * @return whether Comparator is set by class name, not by serializable
1157      * Comparator object.
1158      */
getDuplicateComparatorByClass()1159     public boolean getDuplicateComparatorByClass() {
1160         return duplicateComparatorByClassName;
1161     }
1162 
1163     /**
1164      * @return whether Comparator implements PartialComparator.
1165      */
hasBtreePartialComparator()1166     public boolean hasBtreePartialComparator() {
1167         return btreePartialComparator;
1168     }
1169 
1170     /**
1171      * @return whether Comparator implements PartialComparator.
1172      */
hasDuplicatePartialComparator()1173     public boolean hasDuplicatePartialComparator() {
1174         return duplicatePartialComparator;
1175     }
1176 
1177     /**
1178      * @return whether Comparator implements BinaryEqualityComparator.
1179      */
hasBtreeBinaryEqualityComparator()1180     public boolean hasBtreeBinaryEqualityComparator() {
1181         return btreeBinaryEqualityComparator;
1182     }
1183 
1184     /**
1185      * @return whether Comparator implements BinaryEqualityComparator.
1186      */
hasDuplicateBinaryEqualityComparator()1187     public boolean hasDuplicateBinaryEqualityComparator() {
1188         return duplicateBinaryEqualityComparator;
1189     }
1190 
1191     /**
1192      * Set the db environment after reading in the DatabaseImpl from the log.
1193      */
setEnvironmentImpl(EnvironmentImpl envImpl)1194     public void setEnvironmentImpl(EnvironmentImpl envImpl) {
1195         this.envImpl = envImpl;
1196         initWithEnvironment();
1197         tree.setDatabase(this);
1198     }
1199 
getEnv()1200     public EnvironmentImpl getEnv() {
1201         return envImpl;
1202     }
1203 
1204     /**
1205      * Returns whether one or more handles are open.
1206      */
hasOpenHandles()1207     public boolean hasOpenHandles() {
1208         return referringHandles.size() > 0;
1209     }
1210 
1211     /**
1212      * Add a referring handle
1213      */
addReferringHandle(Database db)1214     public void addReferringHandle(Database db) {
1215         referringHandles.add(db);
1216     }
1217 
1218     /**
1219      * Decrement the reference count.
1220      */
removeReferringHandle(Database db)1221     public void removeReferringHandle(Database db) {
1222         referringHandles.remove(db);
1223     }
1224 
1225     /**
1226      * Returns a copy of the referring database handles.
1227      */
getReferringHandles()1228     public Set<Database> getReferringHandles() {
1229         HashSet<Database> copy = new HashSet<Database>();
1230         synchronized (referringHandles) {
1231             copy.addAll(referringHandles);
1232         }
1233         return copy;
1234     }
1235 
1236     /**
1237      * Called after a handle onto this DB is closed.
1238      */
handleClosed(boolean doSyncDw, boolean deleteTempDb)1239     public void handleClosed(boolean doSyncDw, boolean deleteTempDb)
1240         throws DatabaseException {
1241 
1242         if (referringHandles.isEmpty()) {
1243 
1244             /*
1245              * Transient triggers are discarded when the last handle is
1246              * closed.
1247              */
1248             clearTransientTriggers();
1249 
1250             /*
1251              * Remove a temporary database with no handles open.
1252              *
1253              * We are not synchronized here in any way that would prevent
1254              * another thread from opening a handle during this process, before
1255              * the NameLN is locked.  So we use noWait locking.  If a lock is
1256              * not granted, then another handle was opened and we cannot remove
1257              * the database until later.
1258              *
1259              * We pass the database ID to dbRemove in order to remove the
1260              * database only if the name matches the ID.  This accounts for the
1261              * remote possibility that the database is renamed or another
1262              * database is created with the same name during this process,
1263              * before the NameLN is locked.
1264              *
1265              * We can use a BasicLocker because temporary databases are always
1266              * non-transactional.
1267              */
1268             if (deleteTempDb && isTemporary()) {
1269                 Locker locker =
1270                     BasicLocker.createBasicLocker(envImpl, true /* noWait */);
1271                 boolean operationOk = false;
1272                 try {
1273                     envImpl.getDbTree().dbRemove(locker, getName(), getId());
1274                     operationOk = true;
1275                 } catch (DbTree.NeedRepLockerException e) {
1276                     /* Should never happen; a temp db is never replicated. */
1277                     throw EnvironmentFailureException.unexpectedException(
1278                         envImpl, e);
1279                 } catch (DatabaseNotFoundException e) {
1280                     /* Do nothing if DB was removed or renamed. */
1281                 } catch (LockConflictException e) {
1282                     /*
1283                      * We will have to remove this database later.  Note that
1284                      * we catch LockConflictException for simplicity but we
1285                      * expect either LockNotAvailableException or
1286                      * LockNotGrantedException.
1287                      */
1288                 } catch (Error E) {
1289                     envImpl.invalidate(E);
1290                     throw E;
1291                 } finally {
1292                     locker.operationEnd(operationOk);
1293                 }
1294             }
1295 
1296             /*
1297              * Sync a durable deferred write database with no handles open.  If
1298              * a handle is opened during this process, then the sync may be
1299              * unnecessary but it will not cause a problem.
1300              */
1301             if (doSyncDw && isDurableDeferredWrite()) {
1302                 sync(true);
1303             }
1304         }
1305     }
1306 
1307     /**
1308      * Figure out how much memory is used by the DbFileSummaryMap.  Usually
1309      * this number is built up over time by the DbFileSummaryMap itself and
1310      * added to the memory budget, but in this case we need to reinitialize it
1311      * after recovery, when DbFileSummaryMaps may be cut adrift by the process
1312      * of overlaying new portions of the btree.
1313      */
getTreeAdminMemory()1314     public long getTreeAdminMemory() {
1315         return dbFileSummaries.getMemorySize();
1316     }
1317 
1318     /**
1319      * Update memory budgets when this databaseImpl is closed and will never be
1320      * accessed again or when it is still open when its owning MapLN will be
1321      * garbage collected, due to eviction or recovery.
1322      */
releaseTreeAdminMemory()1323     public void releaseTreeAdminMemory() {
1324 
1325         /*
1326          * There's no need to account for INs which belong to this database,
1327          * because those are closed by the EnvironmentImpl when clearing
1328          * the INList.  Do adjust memory budget for utilization info.
1329          */
1330         dbFileSummaries.subtractFromMemoryBudget();
1331     }
1332 
1333     /**
1334      * @return the referring handle count.
1335      */
getReferringHandleCount()1336     int getReferringHandleCount() {
1337         return referringHandles.size();
1338     }
1339 
1340     /**
1341      * Increments the use count of this DB to prevent it from being evicted.
1342      * Called by the DbTree.createDb/getDb methods that return a DatabaseImpl.
1343      * Must be called while holding a lock on the MapLN. See isInUse. [#13415]
1344      */
incrementUseCount()1345     void incrementUseCount() {
1346         useCount.incrementAndGet();
1347     }
1348 
1349     /**
1350      * Increments the write count and returns the updated value.
1351      * @return updated write count
1352      */
noteWriteHandleOpen()1353     public int noteWriteHandleOpen() {
1354         return writeCount.incrementAndGet();
1355     }
1356 
1357     /**
1358      * Decrements the write count and returns the updated value.
1359      * @return updated write count
1360      */
noteWriteHandleClose()1361     public int noteWriteHandleClose() {
1362         int count = writeCount.decrementAndGet();
1363         assert count >= 0;
1364         return count;
1365     }
1366 
1367     /**
1368      * Decrements the use count of this DB, allowing it to be evicted if the
1369      * use count reaches zero.  Called via DbTree.releaseDb to release a
1370      * DatabaseImpl that was returned by a DbTree.createDb/getDb method. See
1371      * isInUse. [#13415]
1372      */
decrementUseCount()1373     void decrementUseCount() {
1374         assert useCount.get() > 0;
1375         useCount.decrementAndGet();
1376     }
1377 
1378     /**
1379      * Returns whether this DB is in use and cannot be evicted.  Called by
1380      * MapLN.isEvictable while holding a write-lock on the MapLN and a latch on
1381      * its parent BIN. [#13415]
1382      *
1383      * When isInUse returns false (while holding a write-lock on the MapLN and
1384      * a latch on the parent BIN), it guarantees that the database object
1385      * is not in use and cannot be acquired by another thread (via
1386      * DbTree.createDb/getDb) until both the MapLN lock and BIN latch are
1387      * released.  This guarantee is due to the fact that DbTree.createDb/getDb
1388      * only increment the use count while holding a read-lock on the MapLN.
1389      * Therefore, it is safe to evict the MapLN when isInUse returns false.
1390      *
1391      * When isInUse returns true, it is possible that another thread may
1392      * decrement the use count at any time, since no locking or latching is
1393      * performed when calling DbTree.releaseDb (which calls decrementUseCount).
1394      * Therefore, it is not guaranteed that the MapLN is in use when isInUse
1395      * returns true.  A true result means: the DB may be in use, so it is not
1396      * safe to evict it.
1397      */
isInUse()1398     public boolean isInUse() {
1399         return (useCount.get() > 0);
1400     }
1401 
1402     /**
1403      * Checks whether a database is in use during a remove or truncate database
1404      * operation.
1405      */
isInUseDuringDbRemove()1406     boolean isInUseDuringDbRemove() {
1407 
1408         /*
1409          * The use count is at least one here, because remove/truncate has
1410          * called getDb but releaseDb has not yet been called.  Normally the
1411          * database must be closed in order to remove or truncate it and
1412          * referringHandles will be empty.  But when the deprecated
1413          * Database.truncate is called, the database is open and the use count
1414          * includes the number of open handles.  [#15805]
1415          */
1416         return useCount.get() > 1 + referringHandles.size();
1417     }
1418 
1419     /**
1420      * Flush all dirty nodes for this database to disk.
1421      *
1422      * @throws UnsupportedOperationException via Database.sync.
1423      */
sync(boolean flushLog)1424     public synchronized void sync(boolean flushLog)
1425         throws DatabaseException {
1426 
1427         if (!isDurableDeferredWrite()) {
1428             throw new UnsupportedOperationException
1429                 ("Database.sync() is only supported " +
1430                                         "for deferred-write databases");
1431         }
1432 
1433         if (tree.rootExists()) {
1434             envImpl.getCheckpointer().syncDatabase(envImpl, this, flushLog);
1435         }
1436     }
1437 
1438     /**
1439      * For this secondary database return the primary that it is associated
1440      * with, or null if not associated with any primary.  Note that not all
1441      * handles need be associated with a primary.
1442      */
findPrimaryDatabase()1443     public Database findPrimaryDatabase() {
1444         synchronized (referringHandles) {
1445             for (Database obj : referringHandles) {
1446                 if (obj instanceof SecondaryDatabase) {
1447                     return ((SecondaryDatabase) obj).getPrimaryDatabase();
1448                 }
1449             }
1450         }
1451         return null;
1452     }
1453 
getName()1454     public String getName()
1455         throws DatabaseException {
1456 
1457         return envImpl.getDbTree().getDbName(id);
1458     }
1459 
1460     /**
1461      * Returns the DbFileSummary for the given file, allocates it if necessary
1462      * and budgeted memory for any changes.
1463      *
1464      * <p>Must be called under the log write latch.</p>
1465      *
1466      * @param willModify if true, the caller will modify the utilization info.
1467      */
getDbFileSummary(Long fileNum, boolean willModify)1468     public DbFileSummary getDbFileSummary(Long fileNum, boolean willModify) {
1469         if (willModify) {
1470             dirty = true;
1471         }
1472         assert dbFileSummaries != null;
1473 
1474         /*
1475          * Pass true for checkResurrected to prevent memory/disk leaks caused
1476          * by entries that could accumulate for deleted log files.
1477          */
1478         return dbFileSummaries.get(fileNum, true /*adjustMemBudget*/,
1479                                    true /*checkResurrected*/,
1480                                    envImpl.getFileManager());
1481     }
1482 
1483     /**
1484      * Removes the DbFileSummary for the given set of files.
1485      *
1486      * <p>Must be called under the log write latch.</p>
1487      *
1488      * @return whether a DbFileSummary for any of the given files was present
1489      * and was removed.
1490      */
removeDbFileSummaries(Collection<Long> fileNums)1491     public boolean removeDbFileSummaries(Collection<Long> fileNums) {
1492         assert dbFileSummaries != null;
1493         boolean removed = false;
1494         for (Long fileNum : fileNums) {
1495             if (dbFileSummaries.remove(fileNum)) {
1496                 removed = true;
1497             }
1498         }
1499         return removed;
1500     }
1501 
cloneDbFileSummaries()1502     public Map<Long, DbFileSummary> cloneDbFileSummaries() {
1503         return envImpl.getLogManager().cloneDbFileSummaries(this);
1504     }
1505 
1506     /** Called under the log write latch, via cloneDbFileSummaries above. */
cloneDbFileSummariesInternal()1507     public Map<Long, DbFileSummary> cloneDbFileSummariesInternal() {
1508         return dbFileSummaries.cloneMap();
1509     }
1510 
1511     /**
1512      * For unit testing.
1513      */
getDbFileSummaries()1514     public DbFileSummaryMap getDbFileSummaries() {
1515         return dbFileSummaries;
1516     }
1517 
1518     /**
1519      * Returns whether this database has new (unflushed) utilization info or
1520      * the root LSN was modified after it was last logged.
1521      */
isDirty()1522     public boolean isDirty() {
1523         return dirty;
1524     }
1525 
1526     /**
1527      * Sets dirty in order to force the MapLN to be flushed later.
1528      *
1529      * This flag is used when utilization is changed, the root LSN is changed,
1530      * etc, in order to cause the MapLN to be flushed during the next
1531      * checkpoint, or when utilization info is logged.
1532      */
setDirty()1533     public void setDirty() {
1534         dirty = true;
1535     }
1536 
1537     /**
1538      * Returns whether this database's MapLN must be flushed during a
1539      * checkpoint.
1540      */
isCheckpointNeeded()1541     public boolean isCheckpointNeeded() {
1542         return !isDeleted() && (isDirty() || isTemporary());
1543     }
1544 
1545     /**
1546      * @return true if this database is deleted. Delete cleanup may still be in
1547      * progress.
1548      */
isDeleted()1549     public boolean isDeleted() {
1550         return !(deleteState == NOT_DELETED);
1551     }
1552 
1553     /**
1554      * @return true if this database is deleted and all cleanup is finished.
1555      */
isDeleteFinished()1556     public boolean isDeleteFinished() {
1557         return (deleteState == DELETED);
1558     }
1559 
1560     /**
1561      * The delete cleanup is starting. Set this before releasing any
1562      * write locks held for a db operation.
1563      */
startDeleteProcessing()1564     public void startDeleteProcessing() {
1565         assert (deleteState == NOT_DELETED);
1566 
1567         deleteState = DELETED_CLEANUP_INLIST_HARVEST;
1568     }
1569 
1570     /**
1571      * Should be called by the SortedLSNTreeWalker when it is finished with
1572      * the INList.
1573      */
finishedINListHarvest()1574     void finishedINListHarvest() {
1575         assert (deleteState == DELETED_CLEANUP_INLIST_HARVEST);
1576 
1577         deleteState = DELETED_CLEANUP_LOG_HARVEST;
1578     }
1579 
1580     /**
1581      * Perform the entire two-step database deletion.  This method is used at
1582      * non-transactional operation end.  When a transaction is used (see Txn),
1583      * startDeleteProcessing is called at commit before releasing write locks
1584      * and finishDeleteProcessing is called after releasing write locks.
1585      */
startAndFinishDelete()1586     public void startAndFinishDelete()
1587         throws DatabaseException {
1588 
1589         startDeleteProcessing();
1590         finishDeleteProcessing();
1591     }
1592 
1593     /**
1594      * Release the INs for the deleted database, count all log entries for this
1595      * database as obsolete, delete the MapLN, and set the state to DELETED.
1596      *
1597      * Used at transaction end or non-transactional operation end in these
1598      * cases:
1599      *  - purge the deleted database after a commit of
1600      *           Environment.removeDatabase
1601      *  - purge the deleted database after a commit of
1602      *           Environment.truncateDatabase
1603      *  - purge the newly created database after an abort of
1604      *           Environment.truncateDatabase
1605      *
1606      * Note that the processing of the naming tree means the MapLN is never
1607      * actually accessible from the current tree, but deleting the MapLN will
1608      * do two things:
1609      * (a) mark it properly obsolete
1610      * (b) null out the database tree, leaving the INList the only
1611      * reference to the INs.
1612      */
finishDeleteProcessing()1613     public void finishDeleteProcessing()
1614         throws DatabaseException {
1615 
1616         assert TestHookExecute.doHookIfSet(pendingDeletedHook);
1617 
1618         try {
1619             /*
1620              * Delete MapLN before the walk.  Get the root LSN before deleting
1621              * the MapLN, as that will null out the root.
1622              */
1623             long rootLsn = tree.getRootLsn();
1624 
1625             /*
1626              * Grab the in-cache root IN before we call deleteMapLN so that it
1627              * gives us a starting point for the SortedLSNTreeWalk below.  The
1628              * on-disk version is obsolete at this point.
1629              */
1630             IN rootIN = tree.getResidentRootIN(false);
1631             envImpl.getDbTree().deleteMapLN(id);
1632 
1633             /*
1634              * Ensure that the MapLN deletion is flushed to disk, so that
1635              * utilization information is not lost if we crash past this point.
1636              * Note that the Commit entry has already been flushed for the
1637              * transaction of the DB removal/truncation operation, so we cannot
1638              * rely on the flush of the Commit entry to flush the MapLN.
1639              * [#18696]
1640              */
1641             envImpl.getLogManager().flush();
1642 
1643             if (createdAtLogVersion >= 6 &&
1644                 !forceTreeWalkForTruncateAndRemove) {
1645 
1646                 /*
1647                  * For databases created at log version 6 or after, the
1648                  * per-database utilization info is complete and can be counted
1649                  * as obsolete without walking the database.
1650                  *
1651                  * We do not need to flush modified file summaries because the
1652                  * obsolete amounts are logged along with the deleted MapLN and
1653                  * will be re-counted by recovery if necessary.
1654                  */
1655                 envImpl.getLogManager().countObsoleteDb(this);
1656             } else {
1657 
1658                 /*
1659                  * For databases created prior to log version 6, the
1660                  * per-database utilization info is incomplete.  Use the old
1661                  * method of counting utilization via SortedLSNTreeWalker.
1662                  *
1663                  * Use a local tracker that is accumulated under the log write
1664                  * latch when we're done counting.  Start by recording the LSN
1665                  * of the root IN as obsolete.
1666                  */
1667                 LocalUtilizationTracker localTracker =
1668                     new LocalUtilizationTracker(envImpl);
1669                 if (rootLsn != DbLsn.NULL_LSN) {
1670                     localTracker.countObsoleteNodeInexact
1671                         (rootLsn, LogEntryType.LOG_IN, 0, this);
1672                 }
1673 
1674                 /* Fetch LNs to count LN sizes only if so configured. */
1675                 boolean fetchLNSize =
1676                     envImpl.getCleaner().getFetchObsoleteSize(this);
1677 
1678                 /* Use the tree walker to visit every child LSN in the tree. */
1679                 ObsoleteProcessor obsoleteProcessor =
1680                     new ObsoleteProcessor(this, localTracker);
1681                 SortedLSNTreeWalker walker = new ObsoleteTreeWalker
1682                     (this, rootLsn, fetchLNSize, obsoleteProcessor, rootIN);
1683 
1684                 /*
1685                  * At this point, it's possible for the evictor to find an IN
1686                  * for this database on the INList. It should be ignored.
1687                  */
1688                 walker.walk();
1689 
1690                 /*
1691                  * Count obsolete nodes for a deleted database at transaction
1692                  * end time.  Write out the modified file summaries for
1693                  * recovery.
1694                  */
1695                 envImpl.getUtilizationProfile().flushLocalTracker
1696                     (localTracker);
1697             }
1698 
1699             /* Remove all INs for this database from the INList. */
1700             MemoryBudget mb = envImpl.getMemoryBudget();
1701             INList inList = envImpl.getInMemoryINs();
1702             long memoryChange = 0;
1703             try {
1704                 Iterator<IN> iter = inList.iterator();
1705                 while (iter.hasNext()) {
1706                     IN thisIN = iter.next();
1707                     if (thisIN.getDatabase() == this) {
1708                         iter.remove();
1709                         memoryChange +=
1710                             (0 - thisIN.getBudgetedMemorySize());
1711                     }
1712                 }
1713             } finally {
1714                 mb.updateTreeMemoryUsage(memoryChange);
1715             }
1716 
1717             /* Wake up the cleaner to reclaim obsolete disk space. [#22915] */
1718             envImpl.getCleaner().wakeup();
1719         } finally {
1720             /* Adjust memory budget for utilization info. */
1721             dbFileSummaries.subtractFromMemoryBudget();
1722 
1723             deleteState = DELETED;
1724             /* releaseDb to balance getDb called by truncate/remove. */
1725             envImpl.getDbTree().releaseDb(this);
1726         }
1727     }
1728 
1729     /**
1730      * Counts all active LSNs in a database as obsolete.
1731      *
1732      * @param mapLnLsn is the LSN of the MapLN when called via recovery,
1733      * otherwise is NULL_LSN.
1734      *
1735      * <p>Must be called under the log write latch or during recovery.</p>
1736      */
countObsoleteDb(BaseUtilizationTracker tracker, long mapLnLsn)1737     public void countObsoleteDb(BaseUtilizationTracker tracker,
1738                                 long mapLnLsn) {
1739         /*
1740          * Even though the check for createdAtLogVersion and
1741          * forceTreeWalkForTruncateAndRemove is made in finishDeleteProcessing
1742          * before calling this method, we must repeat the check here because
1743          * this method is also called by recovery.
1744          */
1745         if (createdAtLogVersion >= 6 && !forceTreeWalkForTruncateAndRemove) {
1746             tracker.countObsoleteDb(dbFileSummaries, mapLnLsn);
1747         }
1748     }
1749 
1750     private static class ObsoleteTreeWalker extends SortedLSNTreeWalker {
1751 
1752         private final IN rootIN;
1753 
ObsoleteTreeWalker(DatabaseImpl dbImpl, long rootLsn, boolean fetchLNSize, TreeNodeProcessor callback, IN rootIN)1754         private ObsoleteTreeWalker(DatabaseImpl dbImpl,
1755                                    long rootLsn,
1756                                    boolean fetchLNSize,
1757                                    TreeNodeProcessor callback,
1758                                    IN rootIN)
1759             throws DatabaseException {
1760 
1761             super(new DatabaseImpl[] { dbImpl },
1762                   true,  // set INList finish harvest
1763                   new long[] { rootLsn },
1764                   callback,
1765                   null,  /* savedException */
1766                   null); /* exception predicate */
1767 
1768             accumulateLNs = fetchLNSize;
1769             this.rootIN = rootIN;
1770         }
1771 
1772         @Override
getResidentRootIN(@uppressWarningsR) DatabaseImpl ignore)1773         protected IN getResidentRootIN(@SuppressWarnings("unused")
1774                                        DatabaseImpl ignore) {
1775             if (rootIN != null) {
1776                 rootIN.latchShared();
1777             }
1778             return rootIN;
1779         }
1780     }
1781 
1782     /* Mark each LSN obsolete in the utilization tracker. */
1783     private static class ObsoleteProcessor implements TreeNodeProcessor {
1784 
1785         private final LocalUtilizationTracker localTracker;
1786         private final DatabaseImpl db;
1787 
ObsoleteProcessor(DatabaseImpl db, LocalUtilizationTracker localTracker)1788         ObsoleteProcessor(DatabaseImpl db,
1789                           LocalUtilizationTracker localTracker) {
1790             this.db = db;
1791             this.localTracker = localTracker;
1792         }
1793 
1794         @Override
processLSN(long childLsn, LogEntryType childType, Node node, byte[] lnKey, int lastLoggedSize)1795         public void processLSN(long childLsn,
1796                                LogEntryType childType,
1797                                Node node,
1798                                byte[] lnKey,
1799                                int lastLoggedSize) {
1800             assert childLsn != DbLsn.NULL_LSN;
1801 
1802             /*
1803              * Count the LN log size if an LN node and key are available, i.e.,
1804              * we are certain this is an LN. [#15365]
1805              */
1806             int size = 0;
1807             if (lnKey != null && node instanceof LN) {
1808                 size = lastLoggedSize;
1809             }
1810 
1811             localTracker.countObsoleteNodeInexact
1812                 (childLsn, childType, size, db);
1813         }
1814 
1815         @Override
processDirtyDeletedLN(long childLsn, LN ln, @SuppressWarnings(R) byte[] lnKey)1816         public void processDirtyDeletedLN(long childLsn, LN ln,
1817                                           @SuppressWarnings("unused")
1818                                           byte[] lnKey) {
1819             assert ln != null;
1820 
1821             /*
1822              * Do not count the size (pass zero) because the LN is dirty and
1823              * the logged LN is not available.
1824              */
1825             localTracker.countObsoleteNodeInexact
1826                 (childLsn, ln.getGenericLogType(), 0, db);
1827         }
1828 
1829         @Override
noteMemoryExceeded()1830         public void noteMemoryExceeded() {
1831         }
1832     }
1833 
stat(StatsConfig config)1834     public DatabaseStats stat(StatsConfig config)
1835         throws DatabaseException {
1836 
1837         if (stats == null) {
1838 
1839             /*
1840              * Called first time w/ FAST_STATS so just give them an
1841              * empty one.
1842              */
1843             stats = new BtreeStats();
1844         }
1845 
1846         if (!config.getFast()) {
1847             if (tree == null) {
1848                 return new BtreeStats();
1849             }
1850 
1851             PrintStream out = config.getShowProgressStream();
1852             if (out == null) {
1853                 out = System.err;
1854             }
1855 
1856             StatsAccumulator statsAcc =
1857                 new StatsAccumulator(out,
1858                                      config.getShowProgressInterval());
1859             walkDatabaseTree(statsAcc, out, true);
1860             stats.setDbImplStats(statsAcc.getStats());
1861         }
1862         tree.loadStats(config, stats);
1863 
1864         return stats;
1865     }
1866 
1867     /*
1868      * @param config verify configuration
1869      * @param emptyStats empty database stats, to be filled by this method
1870      * @return true if the verify saw no errors.
1871      */
verify(VerifyConfig config, DatabaseStats emptyStats)1872     public boolean verify(VerifyConfig config, DatabaseStats emptyStats)
1873         throws DatabaseException {
1874 
1875         if (tree == null) {
1876             return true;
1877         }
1878 
1879         PrintStream out = config.getShowProgressStream();
1880         if (out == null) {
1881             out = System.err;
1882         }
1883 
1884         StatsAccumulator statsAcc =
1885             new StatsAccumulator(out, config.getShowProgressInterval()) {
1886             @Override
1887             void verifyNode(Node node) {
1888 
1889                 try {
1890                     node.verify(null);
1891                 } catch (DatabaseException INE) {
1892                     progressStream.println(INE);
1893                 }
1894             }
1895         };
1896         boolean ok = walkDatabaseTree(statsAcc, out, config.getPrintInfo());
1897         ((BtreeStats) emptyStats).setDbImplStats(statsAcc.getStats());
1898 
1899         return ok;
1900     }
1901 
1902     /* @return the right kind of stats object for this database. */
getEmptyStats()1903     public DatabaseStats getEmptyStats() {
1904         return new BtreeStats();
1905     }
1906 
1907     /*
1908      * @return true if no errors.
1909      */
walkDatabaseTree(TreeWalkerStatsAccumulator statsAcc, PrintStream out, boolean verbose)1910     private boolean walkDatabaseTree(TreeWalkerStatsAccumulator statsAcc,
1911                                      PrintStream out,
1912                                      boolean verbose)
1913         throws DatabaseException {
1914 
1915         boolean ok = true;
1916         final Locker locker =
1917             LockerFactory.getInternalReadOperationLocker(envImpl);
1918         CursorImpl cursor = null;
1919 
1920         try {
1921             EnvironmentImpl.incThreadLocalReferenceCount();
1922             cursor = new CursorImpl(this, locker);
1923             tree.setTreeStatsAccumulator(statsAcc);
1924 
1925             /*
1926              * This will only be used on the first call for the position()
1927              * call.
1928              */
1929             cursor.setTreeStatsAccumulator(statsAcc);
1930             DatabaseEntry foundData = new DatabaseEntry();
1931             if (getSortedDuplicates()) {
1932                 foundData.setPartial(0, 0, true);
1933             }
1934             DatabaseEntry key = new DatabaseEntry();
1935 
1936             if (cursor.positionFirstOrLast(true /*first*/)) {
1937                 OperationStatus status = cursor.lockAndGetCurrent(
1938                     key, foundData, LockType.NONE, false /*dirtyReadAll*/,
1939                     true /*alreadyLatched*/, true /*releaseLatch*/);
1940                 boolean done = false;
1941                 while (!done) {
1942 
1943                     /* Perform eviction before each cursor operation. */
1944                     envImpl.criticalEviction(false /*backgroundIO*/);
1945 
1946                     try {
1947                         status = cursor.getNext(
1948                             key, foundData, LockType.NONE,
1949                             false /*dirtyReadAll*/, true /*forward*/,
1950                             false /*alreadyLatched*/,
1951                             null /*rangeConstraint*/);
1952                     } catch (DatabaseException e) {
1953                         ok = false;
1954                         if (cursor.advanceCursor(key, foundData)) {
1955                             if (verbose) {
1956                                 out.println("Error encountered (continuing):");
1957                                 out.println(e);
1958                                 printErrorRecord(out, key, foundData);
1959                             }
1960                         } else {
1961                             throw e;
1962                         }
1963                     }
1964                     if (status != OperationStatus.SUCCESS) {
1965                         done = true;
1966                     }
1967                 }
1968             }
1969         } finally {
1970             if (cursor != null) {
1971                 cursor.setTreeStatsAccumulator(null);
1972             }
1973             tree.setTreeStatsAccumulator(null);
1974             EnvironmentImpl.decThreadLocalReferenceCount();
1975 
1976             if (cursor != null) {
1977                 cursor.close();
1978             }
1979 
1980             if (locker != null) {
1981                 locker.operationEnd(ok);
1982             }
1983         }
1984 
1985         return ok;
1986     }
1987 
1988     /**
1989      * Prints the key, if available, for a BIN entry that could not be
1990      * read/verified.  Uses the same format as DbDump and prints both the hex
1991      * and printable versions of the entries.
1992      */
printErrorRecord(PrintStream out, DatabaseEntry key, DatabaseEntry data)1993     private void printErrorRecord(PrintStream out,
1994                                   DatabaseEntry key,
1995                                   DatabaseEntry data) {
1996 
1997         byte[] bytes = key.getData();
1998         StringBuilder sb = new StringBuilder("Error Key ");
1999         if (bytes == null) {
2000             sb.append("UNKNOWN");
2001         } else {
2002             CmdUtil.formatEntry(sb, bytes, false);
2003             sb.append(' ');
2004             CmdUtil.formatEntry(sb, bytes, true);
2005         }
2006         out.println(sb);
2007 
2008         bytes = data.getData();
2009         sb = new StringBuilder("Error Data ");
2010         if (bytes == null) {
2011             sb.append("UNKNOWN");
2012         } else {
2013             CmdUtil.formatEntry(sb, bytes, false);
2014             sb.append(' ');
2015             CmdUtil.formatEntry(sb, bytes, true);
2016         }
2017         out.println(sb);
2018     }
2019 
2020     static class StatsAccumulator implements TreeWalkerStatsAccumulator {
2021         private final Set<Long> inNodeIdsSeen = new HashSet<Long>();
2022         private final Set<Long> binNodeIdsSeen = new HashSet<Long>();
2023         private long[] insSeenByLevel = null;
2024         private long[] binsSeenByLevel = null;
2025         private long[] binEntriesHistogram = null;
2026         private long lnCount = 0;
2027         private long deletedLNCount = 0;
2028         private int mainTreeMaxDepth = 0;
2029 
2030         PrintStream progressStream;
2031         int progressInterval;
2032 
2033         /* The max levels we ever expect to see in a tree. */
2034         private static final int MAX_LEVELS = 100;
2035 
StatsAccumulator(PrintStream progressStream, int progressInterval)2036         StatsAccumulator(PrintStream progressStream,
2037                          int progressInterval) {
2038 
2039             this.progressStream = progressStream;
2040             this.progressInterval = progressInterval;
2041 
2042             insSeenByLevel = new long[MAX_LEVELS];
2043             binsSeenByLevel = new long[MAX_LEVELS];
2044             binEntriesHistogram = new long[10];
2045         }
2046 
verifyNode(@uppressWarningsR) Node node)2047         void verifyNode(@SuppressWarnings("unused") Node node) {
2048         }
2049 
2050         @Override
processIN(IN node, Long nid, int level)2051         public void processIN(IN node, Long nid, int level) {
2052             if (inNodeIdsSeen.add(nid)) {
2053                 tallyLevel(level, insSeenByLevel);
2054                 verifyNode(node);
2055             }
2056         }
2057 
2058         @Override
processBIN(BIN node, Long nid, int level)2059         public void processBIN(BIN node, Long nid, int level) {
2060             if (binNodeIdsSeen.add(nid)) {
2061                 tallyLevel(level, binsSeenByLevel);
2062                 verifyNode(node);
2063                 tallyEntries(node, binEntriesHistogram);
2064             }
2065         }
2066 
tallyLevel(int levelArg, long[] nodesSeenByLevel)2067         private void tallyLevel(int levelArg, long[] nodesSeenByLevel) {
2068             int level = levelArg;
2069             if (level >= IN.MAIN_LEVEL) {
2070                 /* Count DBMAP_LEVEL as main level. [#22209] */
2071                 level &= IN.LEVEL_MASK;
2072                 if (level > mainTreeMaxDepth) {
2073                     mainTreeMaxDepth = level;
2074                 }
2075             }
2076 
2077             nodesSeenByLevel[level]++;
2078         }
2079 
2080         @Override
incrementLNCount()2081         public void incrementLNCount() {
2082             lnCount++;
2083             if (progressInterval != 0) {
2084                 if ((lnCount % progressInterval) == 0) {
2085                     progressStream.println(getStats());
2086                 }
2087             }
2088         }
2089 
2090         @Override
incrementDeletedLNCount()2091         public void incrementDeletedLNCount() {
2092             deletedLNCount++;
2093         }
2094 
tallyEntries(BIN bin, long[] binEntriesHistogram)2095         private void tallyEntries(BIN bin, long[] binEntriesHistogram) {
2096             int nEntries = bin.getNEntries();
2097             int nonDeletedEntries = 0;
2098             for (int i = 0; i < nEntries; i++) {
2099                 /* KD and PD determine deletedness. */
2100                 if (!bin.isEntryPendingDeleted(i) &&
2101                     !bin.isEntryKnownDeleted(i)) {
2102                     nonDeletedEntries++;
2103                 }
2104             }
2105 
2106             int bucket = (nonDeletedEntries * 100) / (bin.getMaxEntries() + 1);
2107             bucket /= 10;
2108             binEntriesHistogram[bucket]++;
2109         }
2110 
getINNodeIdsSeen()2111         Set<Long> getINNodeIdsSeen() {
2112             return inNodeIdsSeen;
2113         }
2114 
getBINNodeIdsSeen()2115         Set<Long> getBINNodeIdsSeen() {
2116             return binNodeIdsSeen;
2117         }
2118 
getINsByLevel()2119         long[] getINsByLevel() {
2120             return insSeenByLevel;
2121         }
2122 
getBINsByLevel()2123         long[] getBINsByLevel() {
2124             return binsSeenByLevel;
2125         }
2126 
getBINEntriesHistogram()2127         long[] getBINEntriesHistogram() {
2128             return binEntriesHistogram;
2129         }
2130 
getLNCount()2131         long getLNCount() {
2132             return lnCount;
2133         }
2134 
getDeletedLNCount()2135         long getDeletedLNCount() {
2136             return deletedLNCount;
2137         }
2138 
getMainTreeMaxDepth()2139         int getMainTreeMaxDepth() {
2140             return mainTreeMaxDepth;
2141         }
2142 
getStats()2143         private StatGroup getStats() {
2144             StatGroup group = new StatGroup(GROUP_NAME, GROUP_DESC);
2145             new LongStat(group, BTREE_IN_COUNT, getINNodeIdsSeen().size());
2146             new LongStat(group, BTREE_BIN_COUNT, getBINNodeIdsSeen().size());
2147             new LongStat(group, BTREE_LN_COUNT, getLNCount());
2148             new LongStat(group, BTREE_DELETED_LN_COUNT, getDeletedLNCount());
2149             new IntStat(group, BTREE_MAINTREE_MAXDEPTH, getMainTreeMaxDepth());
2150             new LongArrayStat(group, BTREE_INS_BYLEVEL, getINsByLevel());
2151             new LongArrayStat(group, BTREE_BINS_BYLEVEL, getBINsByLevel());
2152             new LongArrayStat(group, BTREE_BIN_ENTRIES_HISTOGRAM,
2153                               getBINEntriesHistogram()) {
2154                 @Override
2155                 protected String getFormattedValue() {
2156                     StringBuilder sb = new StringBuilder();
2157                     sb.append("[");
2158                     if (array != null && array.length > 0) {
2159                         boolean first = true;
2160                         for (int i = 0; i < array.length; i++) {
2161                             if (array[i] > 0) {
2162                                 if (!first) {
2163                                     sb.append("; ");
2164                                 }
2165 
2166                                 first = false;
2167                                 int startPct = i * 10;
2168                                 int endPct = (i + 1) * 10 - 1;
2169                                 sb.append(startPct).append("-");
2170                                 sb.append(endPct).append("%: ");
2171                                 sb.append(Stat.FORMAT.format(array[i]));
2172                             }
2173                         }
2174                     }
2175 
2176                     sb.append("]");
2177 
2178                     return sb.toString();
2179                 }
2180             };
2181 
2182             return group;
2183         }
2184     }
2185 
2186     /**
2187      * Preload the cache, using up to maxBytes bytes or maxMillsecs msec.
2188      *
2189      * @throws IllegalArgumentException via Database.preload
2190      */
preload(PreloadConfig config)2191     public PreloadStats preload(PreloadConfig config)
2192         throws DatabaseException {
2193 
2194         return envImpl.preload(new DatabaseImpl[] { this }, config);
2195     }
2196 
2197     /**
2198      * The processLSN() code for CountProcessor.
2199      */
2200     private static class DOSCountCallback
2201         implements DiskOrderedScanner.RecordProcessor {
2202 
2203         public long count = 0;
2204 
2205         @Override
process(byte[] key, byte[] data)2206         public void process(byte[] key, byte[] data) {
2207             assert(key == null);
2208             assert(data == null);
2209             ++count;
2210         }
2211 
2212         @Override
canProcessWithoutBlocking(int nRecords)2213         public boolean canProcessWithoutBlocking(int nRecords) {
2214             return true;
2215         }
2216 
2217         @Override
neverBlocks()2218         public boolean neverBlocks() {
2219             return true;
2220         }
2221     }
2222 
2223     /**
2224      * Count entries in the database including dups, but don't dirty the cache.
2225      */
count(long memoryLimit)2226     public long count(long memoryLimit)
2227         throws DatabaseException {
2228 
2229         try {
2230             MemoryBudget mb = envImpl.getMemoryBudget();
2231 
2232             /*
2233              * Must have at least 1MB of memory to be used by DOS (1MB is
2234              * chosen rather arbitrarely).
2235              */
2236             long minMem = 1024 * 1024;
2237 
2238             /*
2239              * Use a heuristic to calculate the memory limit if none was
2240              * provided by the user. This heuristic makes sure that the
2241              * JE cache will not be affected, but otherwise, it is also
2242              * rather arbitrary.
2243              */
2244             if (memoryLimit <= 0) {
2245                 memoryLimit =
2246                     (mb.getRuntimeMaxMemory() - mb.getMaxMemory()) / 10;
2247             }
2248 
2249             if (memoryLimit < minMem) {
2250                 //System.out.println("Using skip-base Database.count()");
2251                 return count(null, true, null, true);
2252             }
2253 
2254             DOSCountCallback counter = new DOSCountCallback();
2255 
2256             DiskOrderedScanner scanner = new DiskOrderedScanner(
2257                 this, counter, true /*keyOnly*/, true/*countOnly*/,
2258                 Long.MAX_VALUE/*lsnBatchSize*/, memoryLimit);
2259 
2260             try {
2261                 /* Prevent files from being deleted during scan. */
2262                 envImpl.getCleaner().addProtectedFileRange(0L);
2263                 scanner.scan();
2264             } finally {
2265                 /* Allow files to be deleted again. */
2266                 envImpl.getCleaner().removeProtectedFileRange(0L);
2267             }
2268 
2269             if (LatchSupport.TRACK_LATCHES) {
2270                 LatchSupport.expectBtreeLatchesHeld(0);
2271             }
2272 
2273             return counter.count;
2274 
2275         } catch (Error E) {
2276             envImpl.invalidate(E);
2277             throw E;
2278         }
2279     }
2280 
2281     /**
2282      * For future use as API method.  Implementation is incomplete.
2283      *
2284      * Counts entries in a key range by positioning a cursor on the beginning
2285      * key and skipping entries until the ending key is encountered.
2286      */
count( DatabaseEntry beginKey, boolean beginInclusive, DatabaseEntry endKey, boolean endInclusive)2287     private long count(
2288         DatabaseEntry beginKey,
2289         boolean beginInclusive,
2290         DatabaseEntry endKey,
2291         boolean endInclusive) {
2292 
2293         final DatabaseEntry key = new DatabaseEntry();
2294         final DatabaseEntry noData = new DatabaseEntry();
2295         noData.setPartial(0, 0, true);
2296 
2297         final Locker locker = BasicLocker.createBasicLocker(envImpl);
2298         final LockMode lockMode = LockMode.READ_UNCOMMITTED;
2299 
2300         try {
2301             final Cursor c = DbInternal.makeCursor(
2302                 this, locker, null /*cursorConfig*/);
2303 
2304             try {
2305                 /* Position cursor on beginning key. */
2306                 if (beginKey != null) {
2307 
2308                     key.setData(beginKey.getData(), beginKey.getOffset(),
2309                                 beginKey.getSize());
2310 
2311                     if (c.getSearchKeyRange(key, noData, lockMode) !=
2312                         OperationStatus.SUCCESS) {
2313                         return 0;
2314                     }
2315 
2316                     if (!beginInclusive && key.equals(beginKey)) {
2317                         if (c.getNext(key, noData, lockMode) !=
2318                             OperationStatus.SUCCESS) {
2319                             return 0;
2320                         }
2321                     }
2322                 } else {
2323                     if (c.getFirst(key, noData, lockMode) !=
2324                         OperationStatus.SUCCESS) {
2325                         return 0;
2326                     }
2327                 }
2328 
2329                 /* Create RangeConstraint for ending key. */
2330                 RangeConstraint rangeConstraint = null; // INCOMPLETE
2331 
2332                 /* Skip entries to get count. */
2333                 return 1 + DbInternal.getCursorImpl(c).skip(
2334                     true /*forward*/, 0 /*maxCount*/, rangeConstraint);
2335 
2336             } finally {
2337                 c.close();
2338             }
2339         } finally {
2340             locker.operationEnd(true);
2341         }
2342     }
2343 
2344     /*
2345      * Dumping
2346      */
dumpString(int nSpaces)2347     public String dumpString(int nSpaces) {
2348         StringBuilder sb = new StringBuilder();
2349         sb.append(TreeUtils.indent(nSpaces));
2350         sb.append("<database id=\"" );
2351         sb.append(id.toString());
2352         sb.append("\"");
2353         sb.append(" deleteState=\"");
2354         sb.append(deleteState);
2355         sb.append("\"");
2356         sb.append(" useCount=\"");
2357         sb.append(useCount.get());
2358         sb.append("\"");
2359         sb.append(" dupsort=\"");
2360         sb.append(getSortedDuplicates());
2361         sb.append("\"");
2362         sb.append(" temporary=\"");
2363         sb.append(isTemporary());
2364         sb.append("\"");
2365         sb.append(" deferredWrite=\"");
2366         sb.append(isDurableDeferredWrite());
2367         sb.append("\"");
2368         sb.append(" keyPrefixing=\"");
2369         sb.append(getKeyPrefixing());
2370         sb.append("\"");
2371         if (btreeComparator != null) {
2372             sb.append(" btc=\"");
2373             sb.append(getComparatorClassName(btreeComparator,
2374                                              btreeComparatorBytes));
2375             sb.append("\"");
2376             sb.append(" btcPartial=\"");
2377             sb.append(btreePartialComparator);
2378             sb.append("\"");
2379         }
2380         if (duplicateComparator != null) {
2381             sb.append(" dupc=\"");
2382             sb.append(getComparatorClassName(duplicateComparator,
2383                                              duplicateComparatorBytes));
2384             sb.append("\"");
2385             sb.append(" dupcPartial=\"");
2386             sb.append(duplicatePartialComparator);
2387             sb.append("\"");
2388         }
2389         sb.append(">");
2390         if (dbFileSummaries != null) {
2391             Iterator<Map.Entry<Long,DbFileSummary>> entries =
2392                 dbFileSummaries.entrySet().iterator();
2393             while (entries.hasNext()) {
2394                 Map.Entry<Long,DbFileSummary> entry = entries.next();
2395                 Long fileNum = entry.getKey();
2396                 DbFileSummary summary = entry.getValue();
2397                 sb.append("<file file=\"").append(fileNum);
2398                 sb.append("\">");
2399                 sb.append(summary);
2400                 sb.append("/file>");
2401             }
2402         }
2403         sb.append("</database>");
2404         return sb.toString();
2405     }
2406 
2407     /*
2408      * Logging support
2409      */
2410 
2411     /**
2412      * This log entry type is configured to perform marshaling (getLogSize and
2413      * writeToLog) under the write log mutex.  Otherwise, the size could change
2414      * in between calls to these two methods as the result of utilizaton
2415      * tracking.
2416      *
2417      * @see Loggable#getLogSize
2418      */
2419     @Override
getLogSize()2420     public int getLogSize() {
2421 
2422         int size =
2423             id.getLogSize() +
2424             tree.getLogSize() +
2425             1 + // flags, 1 byte
2426             LogUtils.getByteArrayLogSize(btreeComparatorBytes) +
2427             LogUtils.getByteArrayLogSize(duplicateComparatorBytes) +
2428             LogUtils.getPackedIntLogSize(maxTreeEntriesPerNode) +
2429             1;  // createdAtLogVersion
2430 
2431         size += LogUtils.getPackedIntLogSize(dbFileSummaries.size());
2432         Iterator<Map.Entry<Long,DbFileSummary>> i =
2433             dbFileSummaries.entrySet().iterator();
2434         while (i.hasNext()) {
2435             Map.Entry<Long,DbFileSummary> entry = i.next();
2436             Long fileNum = entry.getKey();
2437             DbFileSummary summary = entry.getValue();
2438             size +=
2439                 LogUtils.getPackedLongLogSize(fileNum.longValue()) +
2440                 summary.getLogSize();
2441         }
2442         size += TriggerUtils.logSize(triggerBytes);
2443         return size;
2444     }
2445 
2446     /**
2447      * @see Loggable#writeToLog
2448      */
2449     @Override
writeToLog(ByteBuffer logBuffer)2450     public void writeToLog(ByteBuffer logBuffer) {
2451         id.writeToLog(logBuffer);
2452         tree.writeToLog(logBuffer);
2453         logBuffer.put(flags);
2454         LogUtils.writeByteArray(logBuffer, btreeComparatorBytes);
2455         LogUtils.writeByteArray(logBuffer, duplicateComparatorBytes);
2456         LogUtils.writePackedInt(logBuffer, maxTreeEntriesPerNode);
2457         logBuffer.put(createdAtLogVersion);
2458         LogUtils.writePackedInt(logBuffer, dbFileSummaries.size());
2459         Iterator<Map.Entry<Long,DbFileSummary>> i =
2460             dbFileSummaries.entrySet().iterator();
2461 
2462         while (i.hasNext()) {
2463             Map.Entry<Long,DbFileSummary> entry = i.next();
2464             Long fileNum = entry.getKey();
2465             DbFileSummary summary = entry.getValue();
2466             LogUtils.writePackedLong(logBuffer, fileNum.longValue());
2467             summary.writeToLog(logBuffer);
2468         }
2469 
2470         TriggerUtils.writeTriggers(logBuffer, triggerBytes);
2471 
2472         dirty = false;
2473     }
2474 
2475     /**
2476      * @see Loggable#readFromLog
2477      */
2478     @Override
readFromLog(ByteBuffer itemBuffer, int entryVersion)2479     public void readFromLog(ByteBuffer itemBuffer, int entryVersion) {
2480 
2481         boolean version6OrLater = (entryVersion >= 6);
2482 
2483         id.readFromLog(itemBuffer, entryVersion);
2484         tree.readFromLog(itemBuffer, entryVersion);
2485 
2486         /*
2487          * Versions < 6 have the duplicatesAllowed boolean rather than a flags
2488          * byte here, but we don't need a special case because the old boolean
2489          * value is 1 and replacement flag value is 1.
2490          */
2491         flags = itemBuffer.get();
2492 
2493         if (forceKeyPrefixing) {
2494             setKeyPrefixing();
2495         }
2496 
2497         if (entryVersion >= 2) {
2498             btreeComparatorBytes =
2499                 LogUtils.readByteArray(itemBuffer, !version6OrLater);
2500             duplicateComparatorBytes =
2501                 LogUtils.readByteArray(itemBuffer, !version6OrLater);
2502         } else {
2503             String btreeClassName = LogUtils.readString
2504                 (itemBuffer, !version6OrLater, entryVersion);
2505             String dupClassName = LogUtils.readString
2506                 (itemBuffer, !version6OrLater, entryVersion);
2507             if (btreeClassName.length() == 0) {
2508                 btreeComparatorBytes = LogUtils.ZERO_LENGTH_BYTE_ARRAY;
2509             } else {
2510                 btreeComparatorBytes =
2511                     objectToBytes(btreeClassName, "Btree");
2512             }
2513             if (dupClassName.length() == 0) {
2514                 duplicateComparatorBytes = LogUtils.ZERO_LENGTH_BYTE_ARRAY;
2515             } else {
2516                 duplicateComparatorBytes =
2517                     objectToBytes(dupClassName, "Duplicate");
2518             }
2519         }
2520 
2521         if (entryVersion >= 1) {
2522             maxTreeEntriesPerNode =
2523                 LogUtils.readInt(itemBuffer, !version6OrLater);
2524             if (entryVersion < 8) {
2525                 /* Discard maxDupTreeEntriesPerNode. */
2526                 LogUtils.readInt(itemBuffer, !version6OrLater);
2527             }
2528         }
2529 
2530         if (version6OrLater) {
2531             createdAtLogVersion = itemBuffer.get();
2532             int nFiles = LogUtils.readPackedInt(itemBuffer);
2533             for (int i = 0; i < nFiles; i += 1) {
2534                 long fileNum = LogUtils.readPackedLong(itemBuffer);
2535                 DbFileSummary summary = dbFileSummaries.get
2536                     (Long.valueOf(fileNum), false /*adjustMemBudget*/,
2537                      false /*checkResurrected*/, null /*fileManager*/);
2538                 summary.readFromLog(itemBuffer, entryVersion);
2539             }
2540         }
2541 
2542         triggerBytes = (entryVersion < 8) ?
2543                     null :
2544                     TriggerUtils.readTriggers(itemBuffer, entryVersion);
2545         /* Trigger list is unmarshalled lazily by getTriggers. */
2546     }
2547 
2548     /**
2549      * @see Loggable#dumpLog
2550      */
2551     @Override
dumpLog(StringBuilder sb, boolean verbose)2552     public void dumpLog(StringBuilder sb, boolean verbose) {
2553         sb.append("<database");
2554         dumpFlags(sb, verbose, flags);
2555         sb.append(" btcmp=\"");
2556         sb.append(getComparatorClassName(btreeComparator,
2557                                          btreeComparatorBytes));
2558         sb.append("\"");
2559         sb.append(" dupcmp=\"");
2560         sb.append(getComparatorClassName(duplicateComparator,
2561                                          duplicateComparatorBytes));
2562         sb.append("\" > ");
2563         id.dumpLog(sb, verbose);
2564         tree.dumpLog(sb, verbose);
2565         if (verbose && dbFileSummaries != null) {
2566             Iterator<Map.Entry<Long,DbFileSummary>> entries =
2567                 dbFileSummaries.entrySet().iterator();
2568 
2569             while (entries.hasNext()) {
2570                 Map.Entry<Long,DbFileSummary> entry = entries.next();
2571                 Long fileNum = entry.getKey();
2572                 DbFileSummary summary = entry.getValue();
2573                 sb.append("<file file=\"").append(fileNum);
2574                 sb.append("\">");
2575                 sb.append(summary);
2576                 sb.append("</file>");
2577             }
2578         }
2579         TriggerUtils.dumpTriggers(sb, triggerBytes, getTriggers());
2580         sb.append("</database>");
2581     }
2582 
dumpFlags(StringBuilder sb, @SuppressWarnings(R) boolean verbose, byte flags)2583     static void dumpFlags(StringBuilder sb,
2584                           @SuppressWarnings("unused") boolean verbose,
2585                           byte flags) {
2586         sb.append(" dupsort=\"").append((flags & DUPS_ENABLED) != 0);
2587         sb.append("\" replicated=\"").append((flags & IS_REPLICATED_BIT) != 0);
2588         sb.append("\" temp=\"").append((flags & TEMPORARY_BIT)
2589                                        != 0).append("\" ");
2590     }
2591 
2592     /**
2593      * @see Loggable#getTransactionId
2594      */
2595     @Override
getTransactionId()2596     public long getTransactionId() {
2597         return 0;
2598     }
2599 
2600     /**
2601      * @see Loggable#logicalEquals
2602      * Always return false, this item should never be compared.
2603      */
2604     @Override
logicalEquals(@uppressWarningsR) Loggable other)2605     public boolean logicalEquals(@SuppressWarnings("unused") Loggable other) {
2606         return false;
2607     }
2608 
2609     /**
2610      * Used for log dumping.
2611      */
2612     private static String
getComparatorClassName(Comparator<byte[]> comparator, byte[] comparatorBytes)2613         getComparatorClassName(Comparator<byte[]> comparator,
2614                                byte[] comparatorBytes) {
2615 
2616         if (comparator != null) {
2617             return comparator.getClass().getName();
2618         } else if (comparatorBytes != null &&
2619                    comparatorBytes.length > 0) {
2620 
2621             /*
2622              * Output something for DbPrintLog when
2623              * EnvironmentImpl.getNoComparators.
2624              */
2625             return "byteLen: " + comparatorBytes.length;
2626         } else {
2627             return "";
2628         }
2629     }
2630 
2631     /**
2632      * Used both to read from the log and to validate a comparator when set in
2633      * DatabaseConfig.
2634      */
2635     public static Comparator<byte[]>
instantiateComparator(Class<? extends Comparator<byte[]>> comparatorClass, String comparatorType)2636         instantiateComparator(Class<? extends Comparator<byte[]>>
2637                               comparatorClass,
2638                               String comparatorType) {
2639         if (comparatorClass == null) {
2640             return null;
2641         }
2642 
2643         try {
2644             return comparatorClass.newInstance();
2645         } catch (Exception e) {
2646             throw EnvironmentFailureException.unexpectedException
2647                 ("Exception while trying to load " + comparatorType +
2648                  " Comparator class.", e);
2649         }
2650     }
2651 
2652     /**
2653      * Used to validate a comparator when set in DatabaseConfig.
2654      */
2655     @SuppressWarnings("unchecked")
2656     public Comparator<byte[]>
instantiateComparator(Comparator<byte[]> comparator, String comparatorType)2657         instantiateComparator(Comparator<byte[]> comparator,
2658                               String comparatorType)
2659         throws DatabaseException {
2660 
2661         if (comparator == null) {
2662             return null;
2663         }
2664 
2665         return (Comparator<byte[]>) bytesToObject
2666             (objectToBytes(comparator, comparatorType), comparatorType,
2667              envImpl.getClassLoader());
2668     }
2669 
2670     /**
2671      * Converts a comparator object to a serialized byte array, converting to
2672      * a class name String object if byClassName is true.
2673      *
2674      * @throws EnvironmentFailureException if the object cannot be serialized.
2675      */
comparatorToBytes(Comparator<byte[]> comparator, boolean byClassName, String comparatorType)2676     public static byte[] comparatorToBytes(Comparator<byte[]> comparator,
2677                                            boolean byClassName,
2678                                            String comparatorType) {
2679         if (comparator == null) {
2680             return LogUtils.ZERO_LENGTH_BYTE_ARRAY;
2681         }
2682 
2683         final Object obj =
2684             byClassName ? comparator.getClass().getName() : comparator;
2685 
2686         return objectToBytes(obj, comparatorType);
2687     }
2688 
2689     /**
2690      * Converts an arbitrary object to a serialized byte array.  Assumes that
2691      * the object given is non-null.
2692      */
objectToBytes(Object obj, String comparatorType)2693     public static byte[] objectToBytes(Object obj,
2694                                        String comparatorType) {
2695         try {
2696             ByteArrayOutputStream baos = new ByteArrayOutputStream();
2697             ObjectOutputStream oos = new ObjectOutputStream(baos);
2698             oos.writeObject(obj);
2699             return baos.toByteArray();
2700         } catch (IOException e) {
2701             throw EnvironmentFailureException.unexpectedException
2702                 ("Exception while trying to store " + comparatorType, e);
2703         }
2704     }
2705 
2706     /**
2707      * Converts an arbitrary serialized byte array to an object.  Assumes that
2708      * the byte array given is non-null and has a non-zero length.
2709      */
bytesToObject(byte[] bytes, String comparatorType, ClassLoader loader)2710     static Object bytesToObject(byte[] bytes,
2711                                 String comparatorType,
2712                                 ClassLoader loader) {
2713         try {
2714             ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
2715             ClassResolver.Stream ois = new ClassResolver.Stream(bais, loader);
2716             return ois.readObject();
2717         } catch (Exception e) {
2718             throw EnvironmentFailureException.unexpectedException
2719                 ("Exception while trying to load " + comparatorType, e);
2720         }
2721     }
2722 
compareEntries(DatabaseEntry entry1, DatabaseEntry entry2, boolean duplicates)2723     public int compareEntries(DatabaseEntry entry1,
2724                               DatabaseEntry entry2,
2725                               boolean duplicates) {
2726         return Key.compareKeys
2727             (entry1.getData(), entry1.getOffset(), entry1.getSize(),
2728              entry2.getData(), entry2.getOffset(), entry2.getSize(),
2729              (duplicates ? duplicateComparator : btreeComparator));
2730     }
2731 
2732     /**
2733      * Utility class for converting bytes to compartor or Class.
2734      */
2735     static class ComparatorReader {
2736 
2737         /*
2738          * True if comparator type is Class,
2739          * false if comparator type is Comparator.
2740          */
2741         private final boolean isClass;
2742 
2743         /*
2744          * Record the Class type for this Comparator,
2745          * used by ReplicatedDatabaseConfig.
2746          */
2747         private final Class<? extends Comparator<byte[]>> comparatorClass;
2748         private final Comparator<byte[]> comparator;
2749 
2750         @SuppressWarnings("unchecked")
ComparatorReader(byte[] comparatorBytes, String type, ClassLoader loader)2751         public ComparatorReader(byte[] comparatorBytes,
2752                                 String type,
2753                                 ClassLoader loader) {
2754 
2755             /* No comparator. */
2756             if (comparatorBytes.length == 0) {
2757                 comparatorClass = null;
2758                 comparator = null;
2759                 isClass = false;
2760                 return;
2761             }
2762 
2763             /* Deserialize String class name or Comparator instance. */
2764             final Object obj = bytesToObject(comparatorBytes, type, loader);
2765 
2766             /* Comparator is specified as a class name. */
2767             if (obj instanceof String) {
2768                 final String className = (String)obj;
2769                 try {
2770                     comparatorClass = (Class<? extends Comparator<byte[]>>)
2771                         ClassResolver.resolveClass(className, loader);
2772                 } catch (ClassNotFoundException ee) {
2773                     throw EnvironmentFailureException.
2774                         unexpectedException(ee);
2775                 }
2776                 comparator = instantiateComparator(comparatorClass, type);
2777                 isClass = true;
2778                 return;
2779             }
2780 
2781             /* Comparator is specified as an instance. */
2782             if (obj instanceof Comparator) {
2783                 comparatorClass = null;
2784                 comparator = (Comparator<byte[]>) obj;
2785                 isClass = false;
2786                 return;
2787             }
2788 
2789             /* Should never happen. */
2790             throw EnvironmentFailureException.unexpectedState
2791                 ("Expected class name or Comparator instance, got: " +
2792                  obj.getClass().getName());
2793         }
2794 
isClass()2795         public boolean isClass() {
2796             return isClass;
2797         }
2798 
getComparatorClass()2799         public Class<? extends Comparator<byte[]>> getComparatorClass() {
2800             return comparatorClass;
2801         }
2802 
getComparator()2803         public Comparator<byte[]> getComparator() {
2804             return comparator;
2805         }
2806     }
2807 
getBinDeltaPercent()2808     public int getBinDeltaPercent() {
2809         return binDeltaPercent;
2810     }
2811 
2812     /**
2813      * Return a ReplicationContext that will indicate if this operation
2814      * should broadcast data records for this database as part the replication
2815      * stream.
2816      */
getRepContext()2817     public ReplicationContext getRepContext() {
2818 
2819         /*
2820          * It's sufficient to base the decision on what to return solely on the
2821          * isReplicated() value. We're guaranteed that the environment is
2822          * currently opened w/replication. That's because we refuse to open
2823          * rep'ed environments in standalone mode and we couldn't have created
2824          * this db w/replication specified in a standalone environment.
2825          *
2826          * We also don't have to check if this is a client or master. If this
2827          * method is called, we're executing a write operation that was
2828          * instigated an API call on this node (as opposed to a write operation
2829          * that was instigated by an incoming replication message). We enforce
2830          * elsewhere that write operations are only conducted by the master.
2831          *
2832          * Writes provoked by incoming replication messages are executed
2833          * through the putReplicatedLN and deleteReplicatedLN methods.
2834          */
2835         return isReplicated() ?
2836                ReplicationContext.MASTER :
2837                ReplicationContext.NO_REPLICATE;
2838     }
2839 
2840     /**
2841      * Return a ReplicationContext that includes information on how to
2842      * logically replicate database operations. This kind of replication
2843      * context must be used for any api call which logging a NameLN for that
2844      * represents a database operation. However, NameLNs which are logged for
2845      * other reasons, such as cleaner migration, don't need this special
2846      * replication context.
2847      */
2848     DbOpReplicationContext
getOperationRepContext(DbOperationType operationType, DatabaseId oldDbId)2849         getOperationRepContext(DbOperationType operationType,
2850                                DatabaseId oldDbId) {
2851 
2852         /*
2853          * If this method is called, we're executing a write operation that was
2854          * instigated by an API call on this node (as opposed to a write
2855          * operation that was instigated by an incoming replication
2856          * message). We enforce elsewhere that write operations are only
2857          * conducted by the master.
2858          */
2859         DbOpReplicationContext context =
2860             new DbOpReplicationContext(isReplicated(), operationType);
2861 
2862         if (DbOperationType.isWriteConfigType(operationType)) {
2863             assert(oldDbId == null);
2864             context.setCreateConfig
2865                 (new ReplicatedDatabaseConfig(flags,
2866                                               maxTreeEntriesPerNode,
2867                                               btreeComparatorBytes,
2868                                               duplicateComparatorBytes,
2869                                               triggerBytes));
2870         } else if (operationType == DbOperationType.TRUNCATE) {
2871             assert(oldDbId != null);
2872             context.setTruncateOldDbId(oldDbId);
2873         }
2874         return context;
2875     }
2876 
2877     /**
2878      * Convenience overloading.
2879      *
2880      * @see #getOperationRepContext(DbOperationType, DatabaseId)
2881      * @param operationType
2882      * @return
2883      */
2884     DbOpReplicationContext
getOperationRepContext(DbOperationType operationType)2885         getOperationRepContext(DbOperationType operationType) {
2886 
2887         assert(operationType != DbOperationType.TRUNCATE);
2888         return getOperationRepContext(operationType, null);
2889     }
2890 }
2891