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.evictor; 9 10 import com.sleepycat.je.config.EnvironmentParams; 11 import com.sleepycat.je.dbi.DbConfigManager; 12 import com.sleepycat.je.dbi.EnvironmentImpl; 13 import com.sleepycat.je.dbi.MemoryBudget; 14 import com.sleepycat.je.utilint.TestHook; 15 16 /** 17 * The Arbiter determines whether eviction should occur, by consulting the 18 * memory budget. 19 */ 20 class Arbiter { 21 22 private final MemoryBudget.Totals memBudgetTotals; 23 24 /* Debugging and unit test support. */ 25 private TestHook<Boolean> runnableHook; 26 27 /* je.evictor.evictBytes */ 28 private final long evictBytesSetting; 29 30 /* Whether isCacheFull ever returned true. */ 31 private volatile boolean everFull; 32 Arbiter(EnvironmentImpl envImpl)33 Arbiter(EnvironmentImpl envImpl) { 34 35 DbConfigManager configManager = envImpl.getConfigManager(); 36 37 evictBytesSetting = configManager.getLong( 38 EnvironmentParams.EVICTOR_EVICT_BYTES); 39 40 memBudgetTotals = envImpl.getMemoryBudget().getTotals(); 41 } 42 43 /** 44 * Return true if the memory budget is overspent. 45 */ isOverBudget()46 boolean isOverBudget() { 47 48 return memBudgetTotals.getCacheUsage() > 49 memBudgetTotals.getMaxMemory(); 50 } 51 52 /** 53 * Do a check on whether synchronous eviction is needed. 54 * 55 * Note that this method is intentionally not synchronized in order to 56 * minimize overhead when checking for critical eviction. This method is 57 * called from application threads for every cursor operation. 58 */ needCriticalEviction()59 boolean needCriticalEviction() { 60 61 final long over = memBudgetTotals.getCacheUsage() - 62 memBudgetTotals.getMaxMemory(); 63 64 return (over > memBudgetTotals.getCriticalThreshold()); 65 } 66 67 /** 68 * Do a check on whether the cache should still be subject to eviction. 69 * 70 * Note that this method is intentionally not synchronized in order to 71 * minimize overhead, because it's checked on every iteration of the 72 * evict batch loop. 73 */ stillNeedsEviction()74 boolean stillNeedsEviction() { 75 76 return (memBudgetTotals.getCacheUsage() + evictBytesSetting) > 77 memBudgetTotals.getMaxMemory(); 78 } 79 80 /** 81 * Returns true if the JE cache level is above the point where it is likely 82 * that the cache has filled, and is staying full. This is not guaranteed, 83 * since the level does not stay at a constant value. But it is a good 84 * enough indication to drive activities such as cache mode determination. 85 * This method errs on the side of returning true sooner than the point 86 * where the cache is actually full, as described below. 87 */ isCacheFull()88 public boolean isCacheFull() { 89 90 /* 91 * When eviction occurs, normally the cache level goes down to roughly 92 * MaxMemory minus evictBytesSetting. However, because this is only an 93 * approximation, we double the evictBytesSetting as a fudge factor. 94 * 95 * The idea is to return false from this method only when we're 96 * relatively sure that the cache has not yet filled. This will 97 * prevent the return value from alternating between true and false 98 * repeatedly as the result of normal eviction. 99 */ 100 boolean isNowFull = memBudgetTotals.getCacheUsage() + 101 (2 * evictBytesSetting) >= 102 memBudgetTotals.getMaxMemory(); 103 if (isNowFull) { 104 everFull = true; 105 } 106 return isNowFull; 107 } 108 109 /** 110 * Returns whether eviction has ever occurred, i.e., whether the cache has 111 * ever filled. 112 */ wasCacheEverFull()113 public boolean wasCacheEverFull() { 114 return everFull; 115 } 116 117 /** 118 * Return non zero number of bytes if eviction should happen. Caps the 119 * number of bytes a single thread will try to evict. 120 */ getEvictionPledge()121 long getEvictionPledge() { 122 123 long currentUsage = memBudgetTotals.getCacheUsage(); 124 long maxMem = memBudgetTotals.getMaxMemory(); 125 126 long overBudget = currentUsage - maxMem; 127 boolean doRun = (overBudget > 0); 128 129 long requiredEvictBytes = 0; 130 131 /* If running, figure out how much to evict. */ 132 if (doRun) { 133 requiredEvictBytes = overBudget + evictBytesSetting; 134 /* Don't evict more than 50% of the cache. */ 135 if (currentUsage - requiredEvictBytes < maxMem / 2) { 136 requiredEvictBytes = overBudget + (maxMem / 2); 137 } 138 } 139 140 /* Unit testing, force eviction. */ 141 if (runnableHook != null) { 142 doRun = runnableHook.getHookValue(); 143 if (doRun) { 144 requiredEvictBytes = maxMem; 145 } else { 146 requiredEvictBytes = 0; 147 } 148 } 149 return requiredEvictBytes; 150 } 151 152 /* For unit testing only. */ setRunnableHook(TestHook<Boolean> hook)153 void setRunnableHook(TestHook<Boolean> hook) { 154 runnableHook = hook; 155 } 156 } 157