Skip to content

Commit 2ad390e

Browse files
authored
Merge pull request #5191 from reinhapa/in-memory-rpcfiles-6.x.x
[6.x.x] Adds in memory cache for RPC query results
2 parents 1e6737e + f1d6e02 commit 2ad390e

21 files changed

+989
-401
lines changed

.github/workflows/ci-deploy.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ jobs:
1111
with:
1212
fetch-depth: 1
1313
- name: Set up JDK 8
14-
uses: actions/setup-java@v3
14+
uses: actions/setup-java@v4
1515
with:
1616
distribution: temurin
1717
java-version: '8'
@@ -21,7 +21,7 @@ jobs:
2121
with:
2222
install: true
2323
- name: Cache Maven packages
24-
uses: actions/cache@v3
24+
uses: actions/cache@v4
2525
with:
2626
path: ~/.m2
2727
key: deploy-${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}

.github/workflows/ci-test.yml

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ jobs:
1111
runs-on: ubuntu-latest
1212
steps:
1313
- uses: actions/checkout@v4
14-
- uses: actions/setup-java@v3
14+
- uses: actions/setup-java@v4
1515
with:
1616
distribution: temurin
1717
java-version: ${{ env.DEV_JDK }}
@@ -23,12 +23,15 @@ jobs:
2323
runs-on: ubuntu-latest
2424
steps:
2525
- uses: actions/checkout@v4
26-
- uses: actions/setup-java@v3
26+
- uses: actions/setup-java@v4
2727
with:
2828
distribution: temurin
2929
java-version: ${{ env.DEV_JDK }}
3030
cache: 'maven'
31-
- run: mvn -V -B dependency-check:check
31+
- name: OWASP dependency check
32+
env:
33+
NVD_API_KEY: ${{ secrets.NVD_API_KEY }}
34+
run: mvn -V -B dependency-check:check
3235
timeout-minutes: 60
3336
test:
3437
name: ${{ matrix.os }} Test
@@ -40,7 +43,7 @@ jobs:
4043
steps:
4144
- uses: actions/checkout@v4
4245
- name: Set up JDK
43-
uses: actions/setup-java@v3
46+
uses: actions/setup-java@v4
4447
with:
4548
distribution: temurin
4649
java-version: ${{ env.DEV_JDK }}
@@ -72,7 +75,7 @@ jobs:
7275
run: ${{ steps.install-mvnd.outputs.mvnd-dir }}/mvnd -V -B jacoco:report coveralls:report
7376
- name: Archive build logs
7477
if: always()
75-
uses: actions/upload-artifact@v3
78+
uses: actions/upload-artifact@v4
7679
with:
7780
name: ${{ runner.os }}-build-logs
7881
retention-days: 5

.github/workflows/ci-xqts.yml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,12 @@ jobs:
1010
steps:
1111
- uses: actions/checkout@v4
1212
- name: Set up JDK 8
13-
uses: actions/setup-java@v3
13+
uses: actions/setup-java@v4
1414
with:
1515
distribution: temurin
1616
java-version: '8'
1717
- name: Cache Maven packages
18-
uses: actions/cache@v3
18+
uses: actions/cache@v4
1919
with:
2020
path: ~/.m2
2121
key: xqts-${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
@@ -29,22 +29,22 @@ jobs:
2929
run: find exist-xqts/target -name exist-xqts-runner.sh -exec {} --xqts-version HEAD --output-dir /tmp/xqts-output --exclude-test-case RangeExpr-411d,RangeExpr-409d,RangeExpr-408d,RangeExpr-409c,RangeExpr-408c,GenCompEq-21 \;
3030
- name: Check for HeapDump
3131
id: check_heapdump
32-
uses: andstor/file-existence-action@v2
32+
uses: andstor/file-existence-action@v3
3333
with:
3434
files: "/tmp/*.hprof"
3535
- name: Compress HeapDump
3636
if: steps.check_heapdump.outputs.files_exists == 'true'
3737
run: zstd --rm -9 --progress -T0 /tmp/*.hprof
3838
- name: Attach HeapDump artifact
3939
if: steps.check_heapdump.outputs.files_exists == 'true'
40-
uses: actions/upload-artifact@v3
40+
uses: actions/upload-artifact@v4
4141
with:
4242
name: exist-xqts-runner-hprof
4343
retention-days: 1
4444
path: /tmp/*.hprof.zst
4545
- name: Archive XQTS Logs
4646
if: always()
47-
uses: actions/upload-artifact@v3
47+
uses: actions/upload-artifact@v4
4848
with:
4949
name: xqts-logs
5050
retention-days: 14

exist-core/pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -514,6 +514,11 @@
514514
<artifactId>assertj-core</artifactId>
515515
<scope>test</scope>
516516
</dependency>
517+
<dependency>
518+
<groupId>org.awaitility</groupId>
519+
<artifactId>awaitility</artifactId>
520+
<scope>test</scope>
521+
</dependency>
517522
<dependency>
518523
<groupId>org.easymock</groupId>
519524
<artifactId>easymock</artifactId>

exist-core/src/main/java/org/exist/util/io/ContentFilePool.java

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
2727
import org.exist.util.Configuration;
2828

29+
import javax.annotation.Nullable;
30+
2931
/**
3032
* Generic pool for {@link ContentFile} instances used to represent RPC server data up to a defined size in memory first
3133
* before storing the data in the file system using the {@link TemporaryFileManager}.
@@ -50,23 +52,25 @@ public final class ContentFilePool extends GenericObjectPool<ContentFile> {
5052
* Creates a new pool using the givem temporary file manager, configuration and maximum idle time.
5153
*
5254
* @param tempFileManager the temporary file manager used when need to swap out data bigger than max in memory size
55+
* @param brokerId the optional broker id
5356
* @param config the configuration used to configure the main pool properties
5457
*/
55-
public ContentFilePool(final TemporaryFileManager tempFileManager, final Configuration config) {
56-
super(new ContentFilePoolObjectFactory(tempFileManager, toInMemorySize(config)), toPoolConfig(config));
58+
public ContentFilePool(final TemporaryFileManager tempFileManager, @Nullable final String brokerId, final Configuration config) {
59+
super(new ContentFilePoolObjectFactory(tempFileManager, toInMemorySize(config)), toPoolConfig(brokerId, config));
5760
}
5861

5962
private static int toInMemorySize(final Configuration config) {
6063
return config.getInteger(PROPERTY_IN_MEMORY_SIZE, VirtualTempPath.DEFAULT_IN_MEMORY_SIZE);
6164
}
6265

63-
private static GenericObjectPoolConfig<ContentFile> toPoolConfig(final Configuration config) {
66+
private static GenericObjectPoolConfig<ContentFile> toPoolConfig(@Nullable final String brokerId, final Configuration config) {
6467
final GenericObjectPoolConfig<ContentFile> poolConfig = new GenericObjectPoolConfig<>();
6568
poolConfig.setBlockWhenExhausted(false);
6669
poolConfig.setLifo(true);
6770
poolConfig.setMaxIdle(config.getInteger(PROPERTY_POOL_MAX_IDLE));
6871
poolConfig.setMaxTotal(config.getInteger(PROPERTY_POOL_SIZE));
69-
poolConfig.setJmxNameBase("org.exist.management.exist:type=ContentFilePool");
72+
final String poolName = brokerId == null ? "" : "pool." + brokerId;
73+
poolConfig.setJmxNameBase("org.exist.management.exist:type=ContentFilePool,name=" + poolName);
7074
return poolConfig;
7175
}
7276

exist-core/src/main/java/org/exist/util/io/MemoryContentsInputStream.java

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626

2727
import java.io.IOException;
2828
import java.io.InputStream;
29-
import java.io.OutputStream;
3029
import java.util.concurrent.atomic.AtomicLongFieldUpdater;
3130

3231
/**
@@ -84,7 +83,7 @@ public int read() throws IOException {
8483
public int read(byte[] b, int off, int len) throws IOException {
8584
boolean success = false;
8685
int read = 0;
87-
while (!success) {
86+
while (!success && len > 0) {
8887
long positionBefore = POSITION_UPDATER.get(this);
8988
read = this.memoryContents.read(b, positionBefore, off, len);
9089
if (read < 1) {
@@ -115,13 +114,4 @@ public long skip(long n) throws IOException {
115114
}
116115
return skipped;
117116
}
118-
119-
// Java 9 method, has to compile under Java 1.7 so no @Override
120-
public long transferTo(OutputStream out) throws IOException {
121-
long positionBefore = POSITION_UPDATER.get(this);
122-
long written = this.memoryContents.transferTo(out, positionBefore);
123-
POSITION_UPDATER.set(this, this.memoryContents.size());
124-
return written;
125-
}
126-
127117
}

exist-core/src/main/java/org/exist/util/io/VirtualTempPath.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@ public final class VirtualTempPath implements ContentFile {
5252
@GuardedBy("lock")
5353
private Path contentFile;
5454

55+
public VirtualTempPath(TemporaryFileManager tempFileManager) {
56+
this(DEFAULT_IN_MEMORY_SIZE, tempFileManager);
57+
}
58+
5559
public VirtualTempPath(int inMemorySize, TemporaryFileManager tempFileManager) {
5660
this.inMemorySize = inMemorySize;
5761
this.lock = new StampedLock();
@@ -97,6 +101,7 @@ public OutputStream newOutputStream() throws IOException {
97101
}
98102
}
99103

104+
@Override
100105
public InputStream newInputStream() throws IOException {
101106
long stamp = lock.readLock();
102107
try {
@@ -129,6 +134,7 @@ public void close() {
129134
}
130135
}
131136

137+
@Override
132138
public long size() {
133139
long stamp = lock.readLock();
134140
try {
@@ -141,6 +147,7 @@ public long size() {
141147
}
142148
}
143149

150+
@Override
144151
public byte[] getBytes() {
145152
long stamp = lock.readLock();
146153
try {
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/*
2+
* eXist-db Open Source Native XML Database
3+
* Copyright (C) 2001 The eXist-db Authors
4+
*
5+
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.xmlrpc;
23+
24+
import org.exist.util.io.ContentFile;
25+
26+
import java.util.function.Consumer;
27+
28+
/**
29+
* @author <a href="mailto:[email protected]">Patrick Reinhart</a>
30+
*/
31+
final class CachedContentFile extends AbstractCachedResult {
32+
private final ContentFile result;
33+
private final Consumer<ContentFile> poolConsumer;
34+
35+
CachedContentFile(final ContentFile result, final Consumer<ContentFile> poolConsumer) {
36+
super(0);
37+
this.result = result;
38+
this.poolConsumer = poolConsumer;
39+
}
40+
41+
@Override
42+
public ContentFile getResult() {
43+
return result;
44+
}
45+
46+
@Override
47+
protected void doClose() {
48+
if (result != null) {
49+
poolConsumer.accept(result);
50+
result.close();
51+
}
52+
}
53+
}

exist-core/src/main/java/org/exist/xmlrpc/QueryResultCache.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,9 @@ public QueryResultCache() {
5151
.expireAfterAccess(TIMEOUT, TimeUnit.MILLISECONDS)
5252
.removalListener((key, value, cause) -> {
5353
final AbstractCachedResult qr = (AbstractCachedResult)value;
54-
qr.free(); // must free associated resources
54+
qr.close(); // must close associated resources
5555
if(LOG.isDebugEnabled()) {
56-
LOG.debug("Removing cached result set: {}", new Date(qr.getTimestamp()).toString());
56+
LOG.debug("Removing cached result set: {}", new Date(qr.getTimestamp()));
5757
}
5858
}).build();
5959
}
@@ -81,6 +81,11 @@ public SerializedResult getSerializedResult(final int cacheId) {
8181
return (acr != null && acr instanceof SerializedResult) ? (SerializedResult) acr : null;
8282
}
8383

84+
public CachedContentFile getCachedContentFile(final int cacheId) {
85+
final AbstractCachedResult acr = get(cacheId);
86+
return (acr != null && acr instanceof CachedContentFile) ? (CachedContentFile) acr : null;
87+
}
88+
8489
public void remove(final int cacheId) {
8590
if (cacheId < 0 || cacheId >= cacheIdCounter.get()) {
8691
return; // out of scope

exist-core/src/main/java/org/exist/xmlrpc/RpcAPI.java

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ public interface RpcAPI {
112112
/**
113113
* Retrieve document by name. XML content is indented if prettyPrint is set
114114
* to &gt;=0. Use supplied encoding for output.
115-
*
115+
* <p>
116116
* This method is provided to retrieve a document with encodings other than
117117
* UTF-8. Since the data is handled as binary data, character encodings are
118118
* preserved. byte[]-values are automatically BASE64-encoded by the XMLRPC
@@ -132,7 +132,7 @@ byte[] getDocument(String name, String encoding, int prettyPrint)
132132
* Retrieve document by name. XML content is indented if prettyPrint is set
133133
* to &gt;=0. Use supplied encoding for output and apply the specified
134134
* stylesheet.
135-
*
135+
* <p>
136136
* This method is provided to retrieve a document with encodings other than
137137
* UTF-8. Since the data is handled as binary data, character encodings are
138138
* preserved. byte[]-values are automatically BASE64-encoded by the XMLRPC
@@ -152,7 +152,7 @@ byte[] getDocument(String name, String encoding, int prettyPrint, String stylesh
152152
/**
153153
* Retrieve document by name. All optional output parameters are passed as
154154
* key/value pairs int the <code>parameters</code>.
155-
*
155+
* <p>
156156
* Valid keys may either be taken from
157157
* {@link javax.xml.transform.OutputKeys} or
158158
* {@link org.exist.storage.serializers.EXistOutputKeys}. For example, the
@@ -247,10 +247,10 @@ List<String> getCollectionListing(final String collection)
247247
List<String> getDocumentListing(String collection)
248248
throws EXistException, PermissionDeniedException, URISyntaxException;
249249

250-
Map<String, List> listDocumentPermissions(String name)
250+
Map<String, List<Object>> listDocumentPermissions(String name)
251251
throws EXistException, PermissionDeniedException, URISyntaxException;
252252

253-
Map<XmldbURI, List> listCollectionPermissions(String name)
253+
Map<String, List<Object>> listCollectionPermissions(String name)
254254
throws EXistException, PermissionDeniedException, URISyntaxException;
255255

256256
/**
@@ -497,6 +497,7 @@ byte[] query(
497497
* @throws PermissionDeniedException If the current user is not allowed to perform this action
498498
* @deprecated use List query() or int executeQuery() instead
499499
*/
500+
@Deprecated
500501
Map<String, Object> querySummary(String xquery)
501502
throws EXistException, PermissionDeniedException;
502503

@@ -612,7 +613,7 @@ String uploadCompressed(String file, byte[] data, int length)
612613

613614
/**
614615
* Parse a file previously uploaded with upload.
615-
*
616+
* <p>
616617
* The temporary file will be removed.
617618
*
618619
* @param localFile temporary file name
@@ -749,6 +750,7 @@ Map<String, Object> execute(String path, Map<String, Object> parameters)
749750
*
750751
* @deprecated Use {@link #executeT(String, Map)} instead.
751752
*/
753+
@Deprecated
752754
Map<String, Object> executeT(String path, Map<String, Object> parameters)
753755
throws EXistException, PermissionDeniedException;
754756

@@ -926,7 +928,7 @@ String hasUserLock(String path)
926928

927929
List<String> getGroups() throws EXistException, PermissionDeniedException;
928930

929-
List<List> getIndexedElements(String collectionName, boolean inclusive)
931+
List<List<Object>> getIndexedElements(String collectionName, boolean inclusive)
930932
throws EXistException, PermissionDeniedException, URISyntaxException;
931933

932934
boolean releaseQueryResult(int handle);

0 commit comments

Comments
 (0)