Skip to content

Commit 242af0f

Browse files
committed
[feature] Use of ContentFilePool
Replaces direct construction of in memory RPC results with pooled instances. Signed-off-by: Patrick Reinhart <[email protected]>
1 parent d89b881 commit 242af0f

File tree

6 files changed

+48
-31
lines changed

6 files changed

+48
-31
lines changed

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/xmlrpc/CachedContentFile.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,19 @@
2323

2424
import org.exist.util.io.ContentFile;
2525

26+
import java.util.function.Consumer;
27+
2628
/**
2729
* @author <a href="mailto:[email protected]">Patrick Reinhart</a>
2830
*/
29-
public final class CachedContentFile extends AbstractCachedResult {
31+
final class CachedContentFile extends AbstractCachedResult {
3032
private final ContentFile result;
33+
private final Consumer<ContentFile> poolConsumer;
3134

32-
public CachedContentFile(final ContentFile result) {
35+
CachedContentFile(final ContentFile result, final Consumer<ContentFile> poolConsumer) {
3336
super(0);
3437
this.result = result;
38+
this.poolConsumer = poolConsumer;
3539
}
3640

3741
@Override
@@ -42,6 +46,7 @@ public ContentFile getResult() {
4246
@Override
4347
protected void doClose() {
4448
if (result != null) {
49+
poolConsumer.accept(result);
4550
result.close();
4651
}
4752
}

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

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,8 @@
7373
import org.exist.util.crypto.digest.DigestType;
7474
import org.exist.util.crypto.digest.MessageDigest;
7575
import org.exist.util.io.ContentFile;
76+
import org.exist.util.io.ContentFilePool;
7677
import org.exist.util.io.TemporaryFileManager;
77-
import org.exist.util.io.VirtualTempPath;
7878
import org.exist.util.serializer.SAXSerializer;
7979
import org.exist.util.serializer.SerializerPool;
8080
import org.exist.validation.ValidationReport;
@@ -137,12 +137,14 @@ public class RpcConnection implements RpcAPI {
137137
private final static Charset DEFAULT_ENCODING = StandardCharsets.UTF_8;
138138

139139
private final XmldbRequestProcessorFactory factory;
140+
private final ContentFilePool filePool;
140141
private final Subject user;
141142
private final Random random = new Random();
142143

143-
public RpcConnection(final XmldbRequestProcessorFactory factory, final Subject user) {
144+
public RpcConnection(final XmldbRequestProcessorFactory factory, final ContentFilePool filePool, final Subject user) {
144145
super();
145146
this.factory = factory;
147+
this.filePool = filePool;
146148
this.user = user;
147149
}
148150

@@ -169,10 +171,6 @@ private TemporaryFileManager temporaryFileManager() {
169171
return TemporaryFileManager.getInstance();
170172
}
171173

172-
private VirtualTempPath createVirtualTempPath() {
173-
return new VirtualTempPath(temporaryFileManager());
174-
}
175-
176174
private boolean createCollection(final XmldbURI collUri, final Date created) throws PermissionDeniedException, EXistException {
177175
withDb((broker, transaction) -> {
178176
Collection current = broker.getCollection(collUri);
@@ -704,7 +702,7 @@ public Map<String, Object> getDocumentData(final String docName, final Map<Strin
704702

705703
// A tweak for very large resources, VirtualTempFile
706704
final Map<String, Object> result = new HashMap<>();
707-
final VirtualTempPath tempFile = createVirtualTempPath();
705+
final ContentFile tempFile = filePool.borrowObject();
708706

709707
if (document.getResourceType() == DocumentImpl.XML_FILE) {
710708
try (final OutputStream out = tempFile.newOutputStream();
@@ -724,11 +722,11 @@ public Map<String, Object> getDocumentData(final String docName, final Map<Strin
724722
if (tempFile.size() > MAX_DOWNLOAD_CHUNK_SIZE) {
725723
offset = firstChunk.length;
726724

727-
final int handle = factory.resultSets.add(new CachedContentFile(tempFile));
725+
final int handle = factory.resultSets.add(new CachedContentFile(tempFile, filePool::returnObject));
728726
result.put("handle", Integer.toString(handle));
729727
result.put("supports-long-offset", Boolean.TRUE);
730728
} else {
731-
tempFile.close();
729+
filePool.returnObject(tempFile);
732730
}
733731
result.put("offset", offset);
734732

@@ -2180,7 +2178,7 @@ public Map<String, Object> retrieveFirstChunk(final String docName, final String
21802178
final NodeProxy node = new NodeProxy(null, document, nodeId);
21812179

21822180
final Map<String, Object> result = new HashMap<>();
2183-
final VirtualTempPath tempFile = createVirtualTempPath();
2181+
final ContentFile tempFile = filePool.borrowObject();
21842182

21852183
if (compression && LOG.isDebugEnabled()) {
21862184
LOG.debug("retrieveFirstChunk with compression");
@@ -2199,11 +2197,11 @@ public Map<String, Object> retrieveFirstChunk(final String docName, final String
21992197
if (tempFile.size() > MAX_DOWNLOAD_CHUNK_SIZE) {
22002198
offset = firstChunk.length;
22012199

2202-
final int handle = factory.resultSets.add(new CachedContentFile(tempFile));
2200+
final int handle = factory.resultSets.add(new CachedContentFile(tempFile, filePool::returnObject));
22032201
result.put("handle", Integer.toString(handle));
22042202
result.put("supports-long-offset", Boolean.TRUE);
22052203
} else {
2206-
tempFile.close();
2204+
filePool.returnObject(tempFile);
22072205
}
22082206
result.put("offset", offset);
22092207
return result;
@@ -2283,7 +2281,7 @@ public Map<String, Object> retrieveFirstChunk(final int resultId, final int num,
22832281
}
22842282

22852283
final Map<String, Object> result = new HashMap<>();
2286-
final VirtualTempPath tempFile = createVirtualTempPath();
2284+
final ContentFile tempFile = filePool.borrowObject();
22872285

22882286
if (compression && LOG.isDebugEnabled()) {
22892287
LOG.debug("retrieveFirstChunk with compression");
@@ -2312,11 +2310,11 @@ public Map<String, Object> retrieveFirstChunk(final int resultId, final int num,
23122310
if (tempFile.size() > MAX_DOWNLOAD_CHUNK_SIZE) {
23132311
offset = firstChunk.length;
23142312

2315-
final int handle = factory.resultSets.add(new CachedContentFile(tempFile));
2313+
final int handle = factory.resultSets.add(new CachedContentFile(tempFile, filePool::returnObject));
23162314
result.put("handle", Integer.toString(handle));
23172315
result.put("supports-long-offset", Boolean.TRUE);
23182316
} else {
2319-
tempFile.close();
2317+
filePool.returnObject(tempFile);
23202318
}
23212319
result.put("offset", offset);
23222320
return result;
@@ -2400,7 +2398,7 @@ public Map<String, Object> retrieveAllFirstChunk(final int resultId, final Map<S
24002398
try {
24012399

24022400
final Map<String, Object> result = new HashMap<>();
2403-
final VirtualTempPath tempFile = createVirtualTempPath();
2401+
final ContentFile tempFile = filePool.borrowObject();
24042402

24052403
if (compression && LOG.isDebugEnabled()) {
24062404
LOG.debug("retrieveAllFirstChunk with compression");
@@ -2454,11 +2452,11 @@ public Map<String, Object> retrieveAllFirstChunk(final int resultId, final Map<S
24542452
if (tempFile.size() > MAX_DOWNLOAD_CHUNK_SIZE) {
24552453
offset = firstChunk.length;
24562454

2457-
final int handle = factory.resultSets.add(new CachedContentFile(tempFile));
2455+
final int handle = factory.resultSets.add(new CachedContentFile(tempFile, filePool::returnObject));
24582456
result.put("handle", Integer.toString(handle));
24592457
result.put("supports-long-offset", Boolean.TRUE);
24602458
} else {
2461-
tempFile.close();
2459+
filePool.returnObject(tempFile);
24622460
}
24632461
result.put("offset", offset);
24642462
return result;

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@
3636
import org.exist.security.Subject;
3737
import org.exist.storage.BrokerPool;
3838
import org.exist.util.NamedThreadFactory;
39+
import org.exist.util.io.ContentFilePool;
40+
import org.exist.util.io.TemporaryFileManager;
3941

4042
import java.util.Map;
4143
import java.util.UUID;
@@ -56,6 +58,7 @@ public class XmldbRequestProcessorFactory implements RequestProcessorFactoryFact
5658

5759
private final boolean useDefaultUser;
5860
private final BrokerPool brokerPool;
61+
private final ContentFilePool contentFilePool;
5962
protected final QueryResultCache resultSets = new QueryResultCache();
6063

6164
protected final AtomicLazyVal<ExecutorService> restoreExecutorService;
@@ -72,14 +75,15 @@ public XmldbRequestProcessorFactory(final String databaseId, final boolean useDe
7275
this.databaseId = databaseId;
7376
}
7477
this.brokerPool = BrokerPool.getInstance(this.databaseId);
78+
this.contentFilePool = new ContentFilePool(TemporaryFileManager.getInstance(), brokerPool.getId(), brokerPool.getConfiguration());
7579
this.restoreExecutorService = new AtomicLazyVal<>(() -> Executors.newCachedThreadPool(new NamedThreadFactory(brokerPool, "rpc-db-restore")));
7680
}
7781

7882
@Override
7983
public Object getRequestProcessor(final XmlRpcRequest pRequest) throws XmlRpcException {
8084
final XmlRpcHttpRequestConfig config = (XmlRpcHttpRequestConfig) pRequest.getConfig();
8185
final Subject user = authenticate(config.getBasicUserName(), config.getBasicPassword());
82-
return new RpcConnection(this, user);
86+
return new RpcConnection(this, contentFilePool, user);
8387
}
8488

8589
protected Subject authenticate(String username, String password) throws XmlRpcException {

exist-core/src/test/java/org/exist/util/io/ContentFilePoolTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ void prepare() throws DatabaseConfigurationException {
5555
configuration = new Configuration();
5656
configuration.setProperty(PROPERTY_POOL_SIZE, 1);
5757
configuration.setProperty(PROPERTY_IN_MEMORY_SIZE, 10);
58-
pool = new ContentFilePool(temporaryFileManager, configuration);
58+
pool = new ContentFilePool(temporaryFileManager, null, configuration);
5959
replay(contentFile, temporaryFileManager);
6060
}
6161

exist-core/src/test/java/org/exist/xmlrpc/CachedContentFileTest.java

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929
import org.junit.jupiter.api.Test;
3030
import org.junit.jupiter.api.extension.ExtendWith;
3131

32+
import java.util.function.Consumer;
33+
3234
import static org.easymock.EasyMock.replay;
3335
import static org.easymock.EasyMock.verify;
3436
import static org.assertj.core.api.Assertions.*;
@@ -40,31 +42,35 @@
4042
class CachedContentFileTest {
4143
@Mock
4244
ContentFile contentFile;
45+
@Mock
46+
Consumer<ContentFile> contentFileConsumer;
47+
4348
CachedContentFile cachedContentFile;
4449
CachedContentFile cachedContentFileNoContent;
4550

4651
@BeforeEach
4752
void prepare() {
48-
cachedContentFile = new CachedContentFile(contentFile);
49-
cachedContentFileNoContent= new CachedContentFile(null);
53+
cachedContentFile = new CachedContentFile(contentFile, contentFileConsumer);
54+
cachedContentFileNoContent= new CachedContentFile(null, contentFileConsumer);
5055
}
5156

5257
@AfterEach
5358
void verifyMocks() {
54-
verify(contentFile);
59+
verify(contentFile, contentFileConsumer);
5560
}
5661

5762
@Test
5863
void testGetResult() {
59-
replay(contentFile);
64+
replay(contentFile, contentFileConsumer);
6065
assertThat(cachedContentFile.getResult()).isEqualTo(contentFile);
6166
assertThat(cachedContentFileNoContent.getResult()).isNull();
6267
}
6368

6469
@Test
6570
void testDoClose() {
6671
contentFile.close();
67-
replay(contentFile);
72+
contentFileConsumer.accept(contentFile);
73+
replay(contentFile, contentFileConsumer);
6874
assertThatNoException().isThrownBy(cachedContentFile::doClose);
6975
assertThatNoException().isThrownBy(cachedContentFileNoContent::doClose);
7076
}

0 commit comments

Comments
 (0)