• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

evolvedbinary / elemental / 847

26 Apr 2025 05:03PM UTC coverage: 56.415% (+0.009%) from 56.406%
847

push

circleci

adamretter
[bugfix] Update Codacy badge

28453 of 55846 branches covered (50.95%)

Branch coverage included in aggregate %.

77474 of 131918 relevant lines covered (58.73%)

0.59 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

72.13
/exist-core/src/main/java/org/exist/storage/cache/LRUCache.java
1
/*
2
 * eXist-db Open Source Native XML Database
3
 * Copyright (C) 2001 The eXist-db Authors
4
 *
5
 * info@exist-db.org
6
 * http://www.exist-db.org
7
 *
8
 * This library is free software; you can redistribute it and/or
9
 * modify it under the terms of the GNU Lesser General Public
10
 * License as published by the Free Software Foundation; either
11
 * version 2.1 of the License, or (at your option) any later version.
12
 *
13
 * This library is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16
 * Lesser General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU Lesser General Public
19
 * License along with this library; if not, write to the Free Software
20
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
21
 */
22
package org.exist.storage.cache;
23

24
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
25
import net.jcip.annotations.NotThreadSafe;
26
import org.apache.logging.log4j.LogManager;
27
import org.apache.logging.log4j.Logger;
28
import org.exist.storage.CacheManager;
29
import org.exist.util.hashtable.SequencedLongHashMap;
30

31
import java.util.Iterator;
32

33
/**
34
 * A simple cache implementing a Last Recently Used policy. This
35
 * cache implementation is based on a 
36
 * {@link org.exist.util.hashtable.SequencedLongHashMap}. Contrary
37
 * to the other {@link org.exist.storage.cache.Cache} implementations,
38
 * LRUCache ignores reference counts or timestamps.
39
 * 
40
 * @author wolf
41
 */
42
@NotThreadSafe
43
public class LRUCache<T extends Cacheable> implements Cache<T> {
44
    private final static Logger LOG = LogManager.getLogger(LRUCache.class);
1✔
45

46
        private final String name;
47
        protected int max;
48
    protected final double growthFactor;
49
    protected final Accounting accounting;
50
        protected SequencedLongHashMap<T> map;
51
    private final CacheType type;
52
    private int hitsOld = -1;
1✔
53
    protected CacheManager cacheManager = null;
1✔
54

55
    public LRUCache(final String name, final int size, final double growthFactor, final double growthThreshold, final CacheType type) {
1✔
56
            this.name = name;
1✔
57
                this.max = size;
1✔
58
        this.growthFactor = growthFactor;
1✔
59
        this.accounting = new Accounting(growthThreshold);
1✔
60
        this.accounting.setTotalSize(max);
1✔
61
        this.map = new SequencedLongHashMap<>(size * 2);
1✔
62
        this.type = type;
1✔
63
    }
1✔
64

65
        @Override
66
        public String getName() {
67
                return name;
1✔
68
        }
69

70
        @Override
71
        public void add(final T item, final int initialRefCount) {
72
                _add(item);
1✔
73
        }
1✔
74

75
        @Override
76
    public CacheType getType() {
77
        return type;
1✔
78
    }
79

80
    @Override
81
        public void add(final T item) {
82
        _add(item);
1✔
83
        }
1✔
84

85
        /**
86
     * Avoids StackOverflow through
87
     * base/sub-class overriding add(T)
88
     */
89
        private void _add(final T item) {
90
        if(map.size() == max) {
1✔
91
            removeOne(item);
1✔
92
        }
93
        map.put(item.getKey(), item);
1✔
94
    }
1✔
95

96
        @Override
97
        public T get(final T item) {
98
                return get(item.getKey());
×
99
        }
100

101
        @Override
102
        public T get(final long key) {
103
                final T obj = map.get(key);
1✔
104
                if(obj == null) {
1✔
105
                    accounting.missesIncrement();
1✔
106
                } else {
1✔
107
                    accounting.hitIncrement();
1✔
108
                }
109
                return obj;
1✔
110
        }
111

112
        @Override
113
        public void remove(final T item) {
114
                map.remove(item.getKey());
1✔
115
        }
1✔
116

117
        @Override
118
        public boolean flush() {
119
                boolean flushed = false;
1✔
120
        final Iterator<T> iterator = map.valueIterator();
1✔
121
        while (iterator.hasNext()) {
1✔
122
            final T cacheable = iterator.next();
1✔
123
            if(cacheable.isDirty()) {
1✔
124
                flushed = flushed | cacheable.sync(false);
1✔
125
            }
126
        }
127
                return flushed;
1✔
128
        }
129

130
        
131
    @Override
132
    public boolean hasDirtyItems() {
133
        final Iterator<T> iterator = map.valueIterator();
×
134
        while (iterator.hasNext()) {
×
135
            final T cacheable = iterator.next();
×
136
            if (cacheable.isDirty()) {
×
137
                return true;
×
138
            }
139
        }
140
        return false;
×
141
    }
142
    
143
        @Override
144
        public int getBuffers() {
145
                return max;
1✔
146
        }
147

148
        @Override
149
        public int getUsedBuffers() {
150
                return map.size();
1✔
151
        }
152

153
        @Override
154
        public int getHits() {
155
                return accounting.getHits();
1✔
156
        }
157

158
        @Override
159
        public int getFails() {
160
                return accounting.getMisses();
1✔
161
        }
162
 
163
    public int getThrashing() {
164
        return accounting.getThrashing();
×
165
    }
166

167
        protected void removeOne(final T item) {
168
        boolean removed = false;
1✔
169
        Iterator<Long2ObjectMap.Entry<T>> iterator = map.fastEntrySetIterator();
1✔
170
        do {
171
            final Long2ObjectMap.Entry<T> next = iterator.next();
1✔
172
            final T cached = next.getValue();
1✔
173
            if(cached.allowUnload() && cached.getKey() != item.getKey()) {
1!
174
                cached.sync(true);
1✔
175
                map.remove(next.getLongKey());
1✔
176
                removed = true;
1✔
177
            } else {
1✔
178
                if (!iterator.hasNext()) {
×
179
                    if(LOG.isDebugEnabled()) {
×
180
                        LOG.debug("Unable to remove entry");
×
181
                    }
182
                    // reset the iterator to the beginning
183
                    iterator = map.fastEntrySetIterator();      // TODO(AR) this can cause a never ending loop potentially!
×
184
                }
185
            }
186
        } while(!removed);
1!
187
        accounting.replacedPage(item);
1✔
188
        if (growthFactor > 1.0 && accounting.resizeNeeded()) {
1!
189
            cacheManager.requestMem(this);
×
190
        }
191
        }
1✔
192

193
    @Override
194
    public double getGrowthFactor() {
195
        return growthFactor;
1✔
196
    }
197

198
    @Override
199
    public void setCacheManager(final CacheManager manager) {
200
        this.cacheManager = manager;
1✔
201
    }
1✔
202
    
203
    @Override
204
    public void resize(final int newSize) {
205
        if (newSize < max) {
1!
206
            shrink(newSize);
×
207
        } else {
×
208
            final SequencedLongHashMap<T> newMap = new SequencedLongHashMap<>(newSize * 2);
1✔
209
            newMap.putAll(map);
1✔
210
            max = newSize;
1✔
211
            map = newMap;
1✔
212
            accounting.reset();
1✔
213
            accounting.setTotalSize(max);
1✔
214
        }
215
    }
1✔
216

217
    protected void shrink(final int newSize) {
218
        flush();
×
219
        map = new SequencedLongHashMap<>(newSize);
×
220
        max = newSize;
×
221
        accounting.reset();
×
222
        accounting.setTotalSize(max);
×
223
    }
×
224

225
    @Override
226
    public int getLoad() {
227
        if (hitsOld == 0) {
1✔
228
            hitsOld = accounting.getHits();
1✔
229
            return Integer.MAX_VALUE;
1✔
230
        }
231
        final int load = accounting.getHits() - hitsOld;
1✔
232
        hitsOld = accounting.getHits();
1✔
233
        return load;
1✔
234
    }
235
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc