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