1 8 9 package com.sleepycat.je.tree; 10 11 import java.util.Comparator ; 12 import java.util.Iterator ; 13 import java.util.Set ; 14 15 import com.sleepycat.je.DatabaseException; 16 import com.sleepycat.je.cleaner.Cleaner; 17 import com.sleepycat.je.dbi.CursorImpl; 18 import com.sleepycat.je.dbi.DatabaseId; 19 import com.sleepycat.je.dbi.DatabaseImpl; 20 import com.sleepycat.je.dbi.DbConfigManager; 21 import com.sleepycat.je.dbi.EnvironmentImpl; 22 import com.sleepycat.je.dbi.MemoryBudget; 23 import com.sleepycat.je.log.LogEntryType; 24 import com.sleepycat.je.log.LogManager; 25 import com.sleepycat.je.log.LoggableObject; 26 import com.sleepycat.je.txn.BasicLocker; 27 import com.sleepycat.je.txn.LockGrantType; 28 import com.sleepycat.je.txn.LockResult; 29 import com.sleepycat.je.txn.LockType; 30 import com.sleepycat.je.utilint.DbLsn; 31 import com.sleepycat.je.utilint.TinyHashSet; 32 33 36 public class BIN extends IN implements LoggableObject { 37 38 private static final String BEGIN_TAG = "<bin>"; 39 private static final String END_TAG = "</bin>"; 40 41 44 private TinyHashSet cursorSet; 45 46 49 50 51 private long lastDeltaVersion = DbLsn.NULL_LSN; 52 private int numDeltasSinceLastFull; private boolean prohibitNextDelta; 55 public BIN() { 56 cursorSet = new TinyHashSet(); 57 numDeltasSinceLastFull = 0; 58 prohibitNextDelta = false; 59 } 60 61 public BIN(DatabaseImpl db, 62 byte[] identifierKey, 63 int maxEntriesPerNode, 64 int level) { 65 super(db, identifierKey, maxEntriesPerNode, level); 66 67 cursorSet = new TinyHashSet(); 68 numDeltasSinceLastFull = 0; 69 prohibitNextDelta = false; 70 } 71 72 76 public BINReference createReference() { 77 return new BINReference(getNodeId(), getDatabase().getId(), 78 getIdentifierKey()); 79 } 80 81 85 protected IN createNewInstance(byte[] identifierKey, 86 int maxEntries, 87 int level) { 88 return new BIN(getDatabase(), identifierKey, maxEntries, level); 89 } 90 91 96 boolean isAlwaysLatchedExclusively() { 97 return true; 98 } 99 100 105 public byte[] getChildKey(IN child) 106 throws DatabaseException { 107 108 return child.getDupKey(); 109 } 110 111 114 LogEntryType getBINDeltaType() { 115 return LogEntryType.LOG_BIN_DELTA; 116 } 117 118 122 public long getLastDeltaVersion() { 123 return lastDeltaVersion; 124 } 125 126 130 public void setProhibitNextDelta() { 131 prohibitNextDelta = true; 132 } 133 134 140 protected void descendOnParentSearch(SearchResult result, 141 boolean targetContainsDuplicates, 142 boolean targetIsRoot, 143 long targetNodeId, 144 Node child, 145 boolean requireExactMatch) 146 throws DatabaseException { 147 148 if (child.canBeAncestor(targetContainsDuplicates)) { 149 if (targetContainsDuplicates && targetIsRoot) { 150 151 155 long childNid = child.getNodeId(); 156 ((IN) child).releaseLatch(); 157 158 result.keepSearching = false; 160 if (childNid == targetNodeId) { result.exactParentFound = true; 162 } else { 163 result.exactParentFound = false; 164 } 165 166 170 if (requireExactMatch && ! result.exactParentFound) { 171 result.parent = null; 172 releaseLatch(); 173 } else { 174 result.parent = this; 175 } 176 177 } else { 178 181 releaseLatch(); 182 result.parent = (IN) child; 183 } 184 } else { 185 190 result.exactParentFound = false; 191 result.keepSearching = false; 192 if (!requireExactMatch && targetContainsDuplicates) { 193 result.parent = this; 194 } else { 195 releaseLatch(); 196 result.parent = null; 197 } 198 } 199 } 200 201 205 protected boolean canBeAncestor(boolean targetContainsDuplicates) { 206 207 return targetContainsDuplicates; 208 } 209 210 213 boolean isEvictionProhibited() { 214 return (nCursors() > 0); 215 } 216 217 220 boolean hasNonLNChildren() { 221 222 for (int i = 0; i < getNEntries(); i++) { 223 Node node = getTarget(i); 224 if (node != null) { 225 if (!(node instanceof LN)) { 226 return true; 227 } 228 } 229 } 230 231 return false; 232 } 233 234 237 int getChildEvictionType() { 238 239 Cleaner cleaner = getDatabase().getDbEnvironment().getCleaner(); 240 241 for (int i = 0; i < getNEntries(); i++) { 242 Node node = getTarget(i); 243 if (node != null) { 244 if (node instanceof LN) { 245 if (cleaner.isEvictable(this, i)) { 246 return MAY_EVICT_LNS; 247 } 248 } else { 249 return MAY_NOT_EVICT; 250 } 251 } 252 } 253 return MAY_EVICT_NODE; 254 } 255 256 261 boolean entryZeroKeyComparesLow() { 262 return false; 263 } 264 265 271 public void setKnownDeleted(int index) { 272 273 278 super.setKnownDeleted(index); 279 updateMemorySize(getTarget(index), null); 280 setMigrate(index, false); 281 super.setTarget(index, null); 282 setDirty(true); 283 } 284 285 295 public void setKnownDeletedLeaveTarget(int index) { 296 297 301 setMigrate(index, false); 302 super.setKnownDeleted(index); 303 setDirty(true); 304 } 305 306 310 public void clearKnownDeleted(int index) { 311 super.clearKnownDeleted(index); 312 setDirty(true); 313 } 314 315 316 public static long computeOverhead(DbConfigManager configManager) 317 throws DatabaseException { 318 319 323 return MemoryBudget.BIN_FIXED_OVERHEAD + 324 IN.computeArraysOverhead(configManager); 325 } 326 327 protected long getMemoryOverhead(MemoryBudget mb) { 328 return mb.getBINOverhead(); 329 } 330 331 334 335 336 public Set getCursorSet() { 337 return cursorSet.copy(); 338 } 339 340 344 public void addCursor(CursorImpl cursor) { 345 assert isLatchOwnerForWrite(); 346 cursorSet.add(cursor); 347 } 348 349 355 public void removeCursor(CursorImpl cursor) { 356 assert isLatchOwnerForWrite(); 357 cursorSet.remove(cursor); 358 } 359 360 363 public int nCursors() { 364 return cursorSet.size(); 365 } 366 367 374 BIN getCursorBIN(CursorImpl cursor) { 375 return cursor.getBIN(); 376 } 377 378 BIN getCursorBINToBeRemoved(CursorImpl cursor) { 379 return cursor.getBINToBeRemoved(); 380 } 381 382 int getCursorIndex(CursorImpl cursor) { 383 return cursor.getIndex(); 384 } 385 386 void setCursorBIN(CursorImpl cursor, BIN bin) { 387 cursor.setBIN(bin); 388 } 389 390 void setCursorIndex(CursorImpl cursor, int index) { 391 cursor.setIndex(index); 392 } 393 394 401 void splitSpecial(IN parent, int parentIndex, int maxEntriesPerNode, 402 byte[] key, boolean leftSide) 403 throws DatabaseException { 404 405 int index = findEntry(key, true, false); 406 int nEntries = getNEntries(); 407 boolean exact = (index & IN.EXACT_MATCH) != 0; 408 index &= ~IN.EXACT_MATCH; 409 if (leftSide && 410 index < 0) { 411 splitInternal(parent, parentIndex, maxEntriesPerNode, 1); 412 } else if (!leftSide && 413 !exact && 414 index == (nEntries - 1)) { 415 splitInternal(parent, parentIndex, maxEntriesPerNode, 416 nEntries - 1); 417 } else { 418 split(parent, parentIndex, maxEntriesPerNode); 419 } 420 } 421 422 432 void adjustCursors(IN newSibling, 433 int newSiblingLow, 434 int newSiblingHigh) { 435 assert newSibling.isLatchOwnerForWrite(); 436 assert this.isLatchOwnerForWrite(); 437 int adjustmentDelta = (newSiblingHigh - newSiblingLow); 438 Iterator iter = cursorSet.iterator(); 439 while (iter.hasNext()) { 440 CursorImpl cursor = (CursorImpl) iter.next(); 441 if (getCursorBINToBeRemoved(cursor) == this) { 442 443 447 continue; 448 } 449 int cIdx = getCursorIndex(cursor); 450 BIN cBin = getCursorBIN(cursor); 451 assert cBin == this : 452 "nodeId=" + getNodeId() + 453 " cursor=" + cursor.dumpToString(true); 454 assert newSibling instanceof BIN; 455 456 510 BIN ns = (BIN) newSibling; 511 if (newSiblingLow == 0) { 512 if (cIdx < newSiblingHigh) { 513 514 setCursorBIN(cursor, ns); 515 iter.remove(); 516 ns.addCursor(cursor); 517 } else { 518 519 setCursorIndex(cursor, cIdx - adjustmentDelta); 520 } 521 } else { 522 if (cIdx >= newSiblingLow) { 523 524 setCursorIndex(cursor, cIdx - newSiblingLow); 525 setCursorBIN(cursor, ns); 526 iter.remove(); 527 ns.addCursor(cursor); 528 } 529 } 530 } 531 } 532 533 537 public void verifyCursors() { 538 if (cursorSet != null) { 539 Iterator iter = cursorSet.iterator(); 540 while (iter.hasNext()) { 541 CursorImpl cursor = (CursorImpl) iter.next(); 542 if (getCursorBINToBeRemoved(cursor) != this) { 543 BIN cBin = getCursorBIN(cursor); 544 assert cBin == this; 545 } 546 } 547 } 548 } 549 550 555 void adjustCursorsForInsert(int insertIndex) { 556 assert this.isLatchOwnerForWrite(); 557 559 if (cursorSet != null) { 560 Iterator iter = cursorSet.iterator(); 561 while (iter.hasNext()) { 562 CursorImpl cursor = (CursorImpl) iter.next(); 563 if (getCursorBINToBeRemoved(cursor) != this) { 564 int cIdx = getCursorIndex(cursor); 565 if (insertIndex <= cIdx) { 566 setCursorIndex(cursor, cIdx + 1); 567 } 568 } 569 } 570 } 571 } 572 573 588 void adjustCursorsForMutation(int binIndex, 589 DBIN dupBin, 590 int dupBinIndex, 591 CursorImpl excludeCursor) { 592 assert this.isLatchOwnerForWrite(); 593 595 if (cursorSet != null) { 596 Iterator iter = cursorSet.iterator(); 597 while (iter.hasNext()) { 598 CursorImpl cursor = (CursorImpl) iter.next(); 599 if (getCursorBINToBeRemoved(cursor) != this && 600 cursor != excludeCursor && 601 cursor.getIndex() == binIndex) { 602 assert cursor.getDupBIN() == null; 603 cursor.addCursor(dupBin); 604 cursor.updateDBin(dupBin, dupBinIndex); 605 } 606 } 607 } 608 } 609 610 627 public boolean compress(BINReference binRef, boolean canFetch) 628 throws DatabaseException { 629 630 boolean ret = false; 631 boolean setNewIdKey = false; 632 boolean anyLocksDenied = false; 633 DatabaseImpl db = getDatabase(); 634 BasicLocker lockingTxn = new BasicLocker(db.getDbEnvironment()); 635 636 try { 637 for (int i = 0; i < getNEntries(); i++) { 638 639 656 boolean deleteEntry = false; 657 658 if (binRef == null || 659 isEntryPendingDeleted(i) || 660 isEntryKnownDeleted(i) || 661 binRef.hasDeletedKey(new Key(getKey(i)))) { 662 663 Node n = null; 664 if (canFetch) { 665 n = fetchTarget(i); 666 } else { 667 n = getTarget(i); 668 if (n == null) { 669 670 continue; 671 } 672 } 673 674 if (n == null) { 675 676 deleteEntry = true; 677 } else if (isEntryKnownDeleted(i)) { 678 LockResult lockRet = lockingTxn.nonBlockingLock 679 (n.getNodeId(), LockType.READ, db); 680 if (lockRet.getLockGrant() == LockGrantType.DENIED) { 681 anyLocksDenied = true; 682 continue; 683 } 684 685 deleteEntry = true; 686 } else { 687 if (!n.containsDuplicates()) { 688 LN ln = (LN) n; 689 LockResult lockRet = lockingTxn.nonBlockingLock 690 (ln.getNodeId(), LockType.READ, db); 691 if (lockRet.getLockGrant() == 692 LockGrantType.DENIED) { 693 anyLocksDenied = true; 694 continue; 695 } 696 697 if (ln.isDeleted()) { 698 deleteEntry = true; 699 } 700 } 701 } 702 703 704 if (binRef != null) { 705 binRef.removeDeletedKey(new Key(getKey(i))); 706 } 707 } 708 709 710 if (deleteEntry) { 711 boolean entryIsIdentifierKey = Key.compareKeys 712 (getKey(i), getIdentifierKey(), 713 getKeyComparator()) == 0; 714 if (entryIsIdentifierKey) { 715 716 720 setNewIdKey = true; 721 } 722 723 boolean deleteSuccess = deleteEntry(i, true); 724 assert deleteSuccess; 725 726 730 i--; 731 } 732 } 733 } finally { 734 if (lockingTxn != null) { 735 lockingTxn.operationEnd(); 736 } 737 } 738 739 if (anyLocksDenied && binRef != null) { 740 db.getDbEnvironment().addToCompressorQueue(binRef, false); 741 ret = true; 742 } 743 744 if (getNEntries() != 0 && setNewIdKey) { 745 setIdentifierKey(getKey(0)); 746 } 747 748 749 if (getNEntries() == 0) { 750 setGeneration(0); 751 } 752 753 return ret; 754 } 755 756 public boolean isCompressible() { 757 return true; 758 } 759 760 770 public long evictLNs() 771 throws DatabaseException { 772 773 assert isLatchOwnerForWrite() : 774 "BIN must be latched before evicting LNs"; 775 776 Cleaner cleaner = getDatabase().getDbEnvironment().getCleaner(); 777 778 787 long removed = 0; 788 if (nCursors() == 0) { 789 for (int i = 0; i < getNEntries(); i++) { 790 removed += evictInternal(i, cleaner); 791 } 792 updateMemorySize(removed, 0); 793 } 794 return removed; 795 } 796 797 804 public void evictLN(int index) 805 throws DatabaseException { 806 807 Cleaner cleaner = getDatabase().getDbEnvironment().getCleaner(); 808 long removed = evictInternal(index, cleaner); 809 updateMemorySize(removed, 0); 810 } 811 812 816 private long evictInternal(int index, Cleaner cleaner) 817 throws DatabaseException { 818 819 Node n = getTarget(index); 820 821 822 if (n instanceof LN && 823 cleaner.isEvictable(this, index)) { 824 825 826 LN ln = (LN) n; 827 if (ln.isDirty()) { 828 DatabaseImpl dbImpl = getDatabase(); 829 830 836 assert dbImpl.isDeferredWrite(); 837 838 843 EnvironmentImpl envImpl = dbImpl.getDbEnvironment(); 844 long oldOffset = envImpl.getDeferredWriteTemp() ? 845 getLsn(index) : DbLsn.NULL_LSN; 846 long lsn = ln.log(envImpl, 847 dbImpl.getId(), 848 getKey(index), 849 oldOffset, 0, null, false); updateEntry(index, lsn); 854 } 855 856 857 setTarget(index, null); 858 859 return n.getMemorySizeIncludedByParent(); 860 } else { 861 return 0; 862 } 863 } 864 865 866 boolean validateSubtreeBeforeDelete(int index) 867 throws DatabaseException { 868 869 return true; 870 } 871 872 878 boolean isValidForDelete() 879 throws DatabaseException { 880 881 884 int validIndex = 0; 885 int numValidEntries = 0; 886 boolean needToLatch = !isLatchOwnerForWrite(); 887 try { 888 if (needToLatch) { 889 latch(); 890 } 891 for (int i = 0; i < getNEntries(); i++) { 892 if (!isEntryKnownDeleted(i)) { 893 numValidEntries++; 894 validIndex = i; 895 } 896 } 897 898 if (numValidEntries > 1) { return false; 900 } else { 901 if (nCursors() > 0) { return false; 903 } 904 if (numValidEntries == 1) { Node child = fetchTarget(validIndex); 906 if (child == null) { 907 return false; 908 } 909 child.latchShared(); 910 boolean ret = child.isValidForDelete(); 911 child.releaseLatch(); 912 return ret; 913 } else { 914 return true; } 916 } 917 } finally { 918 if (needToLatch && 919 isLatchOwnerForWrite()) { 920 releaseLatch(); 921 } 922 } 923 } 924 925 928 void accumulateStats(TreeWalkerStatsAccumulator acc) { 929 acc.processBIN(this, new Long (getNodeId()), getLevel()); 930 } 931 932 937 public Comparator getKeyComparator() { 938 return getDatabase().getBtreeComparator(); 939 } 940 941 public String beginTag() { 942 return BEGIN_TAG; 943 } 944 945 public String endTag() { 946 return END_TAG; 947 } 948 949 952 953 956 public void logDirtyChildren() 957 throws DatabaseException { 958 959 960 EnvironmentImpl envImpl = getDatabase().getDbEnvironment(); 961 for (int i = 0; i < getNEntries(); i++) { 962 Node node = getTarget(i); 963 if (node != null) { 964 965 if (node instanceof LN) { 966 LN ln = (LN) node; 967 968 969 if (ln.isDirty()) { 970 971 977 long oldOffset = envImpl.getDeferredWriteTemp() ? 978 getLsn(i) : DbLsn.NULL_LSN; 979 long childLsn = ln.log(envImpl, 980 getDatabase().getId(), 981 getKey(i), 982 oldOffset, 0, null, true); updateEntry(i, childLsn); 987 } 988 989 } else { 990 DIN din = (DIN) node; 991 din.latch(false); 992 try { 993 if (din.getDirty()) { 994 din.logDirtyChildren(); 995 long childLsn = 996 din.log(envImpl.getLogManager(), 997 false, true, false, true, this); updateEntry(i, childLsn); 1003 } 1004 } finally { 1005 din.releaseLatch(); 1006 } 1007 } 1008 } 1009 } 1010 } 1011 1012 1015 public LogEntryType getLogType() { 1016 return LogEntryType.LOG_BIN; 1017 } 1018 1019 public String shortClassName() { 1020 return "BIN"; 1021 } 1022 1023 1027 protected long logInternal(LogManager logManager, 1028 boolean allowDeltas, 1029 boolean isProvisional, 1030 boolean proactiveMigration, 1031 boolean backgroundIO, 1032 IN parent) 1033 throws DatabaseException { 1034 1035 boolean doDeltaLog = false; 1036 long lastFullVersion = getLastFullVersion(); 1037 1038 1039 Cleaner cleaner = getDatabase().getDbEnvironment().getCleaner(); 1040 cleaner.lazyMigrateLNs(this, proactiveMigration, backgroundIO); 1041 1042 1043 if (getDatabase().isDeferredWrite()) { 1044 logDirtyLNs(logManager); 1045 } 1046 1047 1056 BINDelta deltaInfo = null; 1057 if ((allowDeltas) && 1058 (lastFullVersion != DbLsn.NULL_LSN) && 1059 !prohibitNextDelta && 1060 !getDatabase().isDeferredWrite()) { 1061 deltaInfo = new BINDelta(this); 1062 doDeltaLog = doDeltaLog(deltaInfo); 1063 } 1064 1065 long returnLsn = DbLsn.NULL_LSN; 1066 if (doDeltaLog) { 1067 1068 1072 lastDeltaVersion = logManager.log 1073 (deltaInfo, false, backgroundIO, DbLsn.NULL_LSN, 0); 1075 returnLsn = DbLsn.NULL_LSN; 1076 numDeltasSinceLastFull++; 1077 } else { 1078 1079 returnLsn = super.logInternal 1080 (logManager, allowDeltas, isProvisional, proactiveMigration, 1081 backgroundIO, parent); 1082 lastDeltaVersion = DbLsn.NULL_LSN; 1083 numDeltasSinceLastFull = 0; 1084 } 1085 prohibitNextDelta = false; 1086 1087 return returnLsn; 1088 } 1089 1090 private void logDirtyLNs(LogManager logManager) 1091 throws DatabaseException { 1092 1093 DatabaseId dbId = getDatabase().getId(); 1094 EnvironmentImpl envImpl = getDatabase().getDbEnvironment(); 1095 1096 for (int i = 0; i < getNEntries(); i++) { 1097 Node node = getTarget(i); 1098 if ((node != null) && (node instanceof LN)) { 1099 1100 LN ln = (LN) node; 1101 if (ln.isDirty()) { 1102 1103 1109 assert getDatabase().isDeferredWrite(); 1110 1111 1117 long oldOffset = envImpl.getDeferredWriteTemp() ? 1118 getLsn(i) : DbLsn.NULL_LSN; 1119 long lsn = ln.log(envImpl, 1120 dbId, 1121 getKey(i), 1122 null, oldOffset, 0, null, true, false); updateEntry(i, lsn); 1129 } 1130 } 1131 } 1132 } 1133 1134 1141 private boolean doDeltaLog(BINDelta deltaInfo) 1142 throws DatabaseException { 1143 1144 int maxDiffs = (getNEntries() * 1145 getDatabase().getBinDeltaPercent())/100; 1146 if ((deltaInfo.getNumDeltas() <= maxDiffs) && 1147 (numDeltasSinceLastFull < getDatabase().getBinMaxDeltas())) { 1148 return true; 1149 } else { 1150 return false; 1151 } 1152 } 1153} 1154 | Popular Tags |