postgresql-jdbc.git / summary / log / commit / refs
commit 191ccf2ceca2e4159027de0ff5eaed4a4b4f1fe1
Author: Vladimir Sitnikov <sitnikov.vladimir@gmail.com>
Date: Fri Aug 19 11:58:42 2016 +0000
fix: properly account cache size when duplicate entries returned to the cache
This might happen for cases like
String sql = ...;
a = con.prepareStatement(sql);
b = con.prepareStatement(sql);
a.execute();
a.close();
b.execute();
b.close(); // <-- here an entry with the same key is returned to the cache
.../main/java/org/postgresql/util/LruCache.java | 10 ++-
.../org/postgresql/test/util/LruCacheTest.java | 97 ++++++++++++++--------
2 files changed, 72 insertions(+), 35 deletions(-)
diff --git a/pgjdbc/src/main/java/org/postgresql/util/LruCache.java b/pgjdbc/src/main/java/org/postgresql/util/LruCache.java
index 603be5bf..deda25d1 100644
--- a/pgjdbc/src/main/java/org/postgresql/util/LruCache.java
+++ b/pgjdbc/src/main/java/org/postgresql/util/LruCache.java
@@ -136,7 +136,15 @@ public class LruCache<Key, Value extends CanEstimateSize> {
return;
}
currentSize += valueSize;
- cache.put(key, value);
+ Value prev = cache.put(key, value);
+ if (prev == null) {
+ return;
+ }
+ // This should be a rare case
+ currentSize -= prev.getSize();
+ if (prev != value) {
+ evictValue(prev);
+ }
}
public final static CreateAction NOOP_CREATE_ACTION = new CreateAction() {
diff --git a/pgjdbc/src/test/java/org/postgresql/test/util/LruCacheTest.java b/pgjdbc/src/test/java/org/postgresql/test/util/LruCacheTest.java
index e3da3410..54ded3ab 100644
--- a/pgjdbc/src/test/java/org/postgresql/test/util/LruCacheTest.java
+++ b/pgjdbc/src/test/java/org/postgresql/test/util/LruCacheTest.java
@@ -48,7 +48,7 @@ public class LruCacheTest extends TestCase {
@Override
protected void setUp() throws Exception {
- cache = new LruCache<Integer, Entry>(3, 1000, false, new LruCache.CreateAction<Integer, Entry>() {
+ cache = new LruCache<Integer, Entry>(4, 1000, false, new LruCache.CreateAction<Integer, Entry>() {
@Override
public Entry create(Integer key) throws SQLException {
assertEquals("Unexpected create", expectCreate[0], key);
@@ -57,6 +57,9 @@ public class LruCacheTest extends TestCase {
}, new LruCache.EvictAction<Entry>() {
@Override
public void evict(Entry entry) throws SQLException {
+ if (expectEvict.isEmpty()) {
+ fail("Unexpected entry was evicted: " + entry);
+ }
Entry expected = expectEvict.removeFirst();
assertEquals("Unexpected evict", expected, entry);
}
@@ -68,25 +71,24 @@ public class LruCacheTest extends TestCase {
Entry b;
Entry c;
Entry d;
+ Entry e;
- a = use(1, dummy);
- b = use(2, dummy);
- c = use(3, dummy);
- d = use(4, a);
+ a = use(1);
+ b = use(2);
+ c = use(3);
+ d = use(4);
+ e = use(5, a);
}
public void testEvictsBySize() throws SQLException {
Entry a;
Entry b;
Entry c;
- Entry d;
- a = use(3, dummy);
- b = use(5, dummy);
- c = use((int) (1000 - a.getSize() - b.getSize()), dummy);
- // Now cache holds exactly 1000 bytes.
- // a and b should be evicted
- d = use(4, a, b);
+ a = use(330);
+ b = use(331);
+ c = use(332);
+ use(400, a, b);
}
public void testEvictsLeastRecentlyUsed() throws SQLException {
@@ -95,10 +97,11 @@ public class LruCacheTest extends TestCase {
Entry c;
Entry d;
- a = use(1, dummy);
- b = use(2, dummy);
- c = use(3, dummy);
- a = use(1, dummy); // reuse a
+ a = use(1);
+ b = use(2);
+ c = use(3);
+ a = use(1); // reuse a
+ use(5);
d = use(4, b); // expect b to be evicted
}
@@ -107,37 +110,55 @@ public class LruCacheTest extends TestCase {
Entry b;
Entry c;
Entry d;
+ Entry e;
- a = use(1, dummy);
- b = use(2, dummy);
- c = use(3, dummy);
- d = use(4, a);
+ a = use(1);
+ b = use(2);
+ c = use(3);
+ d = use(4);
+ e = use(5, a);
- for (int i = 0; i < 100000; i++) {
+ for (int i = 0; i < 1000; i++) {
a = use(1, b);
b = use(2, c);
c = use(3, d);
- d = use(4, a);
+ d = use(4, e);
+ e = use(5, a);
}
}
+ public void testDuplicateKey() throws SQLException {
+ Entry a;
+
+ a = use(1);
+ expectEvict.clear();
+ expectEvict.add(a);
+ // This overwrites the cache, evicting previous entry with exactly the same key
+ cache.put(1, new Entry(1));
+ assertEvict();
+ }
+
public void testCaching() throws SQLException {
Entry a;
Entry b;
Entry c;
Entry d;
-
- a = use(1, dummy);
- b = use(2, dummy);
- c = use(3, dummy);
-
- for (int i = 0; i < 100000; i++) {
- b = use(-2, dummy);
- a = use(-1, dummy);
- d = use(4, c);
- b = use(-2, dummy);
- a = use(-1, dummy);
- c = use(3, d);
+ Entry e;
+
+ a = use(1);
+ b = use(2);
+ c = use(3);
+ d = use(4);
+
+ for (int i = 0; i < 10000; i++) {
+ c = use(-3);
+ b = use(-2);
+ a = use(-1);
+ e = use(5, d);
+ c = use(-3);
+ b = use(-2);
+ a = use(-1);
+ d = use(4, e);
}
}
@@ -147,6 +168,14 @@ public class LruCacheTest extends TestCase {
this.expectEvict.addAll(Arrays.asList(expectEvict));
Entry a = cache.borrow(Math.abs(expectCreate));
cache.put(a.id, a); // a
+ assertEvict();
return a;
}
+
+ private void assertEvict() {
+ if (expectEvict.isEmpty()) {
+ return;
+ }
+ fail("Some of the expected evictions not happened: " + expectEvict.toString());
+ }
}
[parent: 17c8afb11c31]