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