Precompute h2 persisted cache space used per entry
The problem is h2 cannot execute concurrent queries on the same
database. The query to get the disk stats(count and space) is a long
lasting query which blocks any other queries to the same cache. The disk
stats are needed when pruning the cache (every day at 01:00) and when
caches are listed, either using the REST API or the show-caches ssh
command.
Here is an example of the problem: diff_intraline of 1GB limit with 1.6
million entries can take up to 16 seconds to compute disk stats. During
that time, any request that needs to get/insert data into the diff
intraline will be blocked until disk stats are done. Every time an admin
executes the show-caches command, a lot of requests can be blocked for a
long time; (number of big persisted caches) x (time to compute stats)
The problem is even worse during pruning because right after computing
the space, another long lasting query is executed; select every row and
associated space used.
Precomputing the space used improves the situation. In the previous
example, it reduces the query time from 16 to 4 seconds.
The real fix would be to also precompute the sum of space used by all
entries in a separate table but this is more complex since it would
involve triggers to keep the sum up to date when a row is inserted,
updated or deleted. Even with this, it would still not be perfect, in the
previous example, it reduces the query time from 4 to 2.5 seconds. The
number of rows also needs to be precomputed, but precomputing this would
be wrong since counting number of rows should be quick in a database; it
should not be implemented in the schema.
This change improves the situation by cutting the query time by 4 with
minimal change. A real fix will be done in another change.
Bug: Issue 5876
Change-Id: I38805688289a474aa719c52ab03079cbf11d1c5c
diff --git a/gerrit-cache-h2/src/main/java/com/google/gerrit/server/cache/h2/H2CacheImpl.java b/gerrit-cache-h2/src/main/java/com/google/gerrit/server/cache/h2/H2CacheImpl.java
index b0c7764..97c540e 100644
--- a/gerrit-cache-h2/src/main/java/com/google/gerrit/server/cache/h2/H2CacheImpl.java
+++ b/gerrit-cache-h2/src/main/java/com/google/gerrit/server/cache/h2/H2CacheImpl.java
@@ -504,7 +504,8 @@
try {
c = acquire();
if (c.put == null) {
- c.put = c.conn.prepareStatement("MERGE INTO data VALUES(?,?,?,?)");
+ c.put = c.conn.prepareStatement(
+ "MERGE INTO data (k, v, created, accessed) VALUES(?,?,?,?)");
}
try {
keyType.set(c.put, 1, key);
@@ -572,7 +573,7 @@
try (Statement s = c.conn.createStatement()) {
long used = 0;
try (ResultSet r =
- s.executeQuery("SELECT SUM(OCTET_LENGTH(k) + OCTET_LENGTH(v)) FROM data")) {
+ s.executeQuery("SELECT SUM(space) FROM data")) {
used = r.next() ? r.getLong(1) : 0;
}
if (used <= maxSize) {
@@ -583,7 +584,7 @@
s.executeQuery(
"SELECT"
+ " k"
- + ",OCTET_LENGTH(k) + OCTET_LENGTH(v)"
+ + ",space"
+ ",created"
+ " FROM data"
+ " ORDER BY accessed")) {
@@ -618,7 +619,7 @@
s.executeQuery(
"SELECT"
+ " COUNT(*)"
- + ",SUM(OCTET_LENGTH(k) + OCTET_LENGTH(v))"
+ + ",SUM(space)"
+ " FROM data")) {
if (r.next()) {
size = r.getLong(1);
@@ -670,7 +671,7 @@
this.url = url;
this.conn = org.h2.Driver.load().connect(url, null);
try (Statement stmt = conn.createStatement()) {
- stmt.execute(
+ stmt.addBatch(
"CREATE TABLE IF NOT EXISTS data"
+ "(k "
+ type.columnType()
@@ -679,6 +680,10 @@
+ ",created TIMESTAMP NOT NULL"
+ ",accessed TIMESTAMP NOT NULL"
+ ")");
+ stmt.addBatch(
+ "ALTER TABLE data ADD COLUMN IF NOT EXISTS "
+ + "space BIGINT AS OCTET_LENGTH(k) + OCTET_LENGTH(v)");
+ stmt.executeBatch();
}
}