Skip to content

Commit 46dfeb1

Browse files
authored
Cache and reuse EntityManagerFactory (#104)
1 parent e81a2c4 commit 46dfeb1

File tree

2 files changed

+50
-15
lines changed

2 files changed

+50
-15
lines changed

extension/persistence/eclipselink/src/main/java/io/polaris/extension/persistence/impl/eclipselink/PolarisEclipseLinkMetaStoreSessionImpl.java

Lines changed: 46 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -54,17 +54,21 @@
5454
import java.util.HashMap;
5555
import java.util.List;
5656
import java.util.Map;
57+
import java.util.concurrent.ConcurrentHashMap;
5758
import java.util.function.Function;
5859
import java.util.function.Predicate;
5960
import java.util.function.Supplier;
6061
import java.util.stream.Collectors;
6162
import javax.xml.parsers.DocumentBuilder;
6263
import javax.xml.parsers.DocumentBuilderFactory;
64+
import javax.xml.parsers.ParserConfigurationException;
6365
import javax.xml.xpath.XPath;
6466
import javax.xml.xpath.XPathConstants;
67+
import javax.xml.xpath.XPathExpressionException;
6568
import javax.xml.xpath.XPathFactory;
6669
import org.jetbrains.annotations.NotNull;
6770
import org.jetbrains.annotations.Nullable;
71+
import org.jetbrains.annotations.TestOnly;
6872
import org.slf4j.Logger;
6973
import org.slf4j.LoggerFactory;
7074
import org.w3c.dom.Document;
@@ -80,6 +84,10 @@ public class PolarisEclipseLinkMetaStoreSessionImpl implements PolarisMetaStoreS
8084
private static final Logger LOG =
8185
LoggerFactory.getLogger(PolarisEclipseLinkMetaStoreSessionImpl.class);
8286

87+
// Cache to hold the EntityManagerFactory for each realm. Each realm needs a separate
88+
// EntityManagerFactory since it connects to different databases
89+
private static final ConcurrentHashMap<String, EntityManagerFactory> realmFactories =
90+
new ConcurrentHashMap<>();
8391
private final EntityManagerFactory emf;
8492
private final ThreadLocal<EntityManager> localSession = new ThreadLocal<>();
8593
private final PolarisEclipseLinkStore store;
@@ -100,20 +108,30 @@ public PolarisEclipseLinkMetaStoreSessionImpl(
100108
@NotNull RealmContext realmContext,
101109
@Nullable String confFile,
102110
@Nullable String persistenceUnitName) {
103-
104-
emf = createEntityManagerFactory(realmContext, confFile, persistenceUnitName);
105111
LOG.debug("Create EclipseLink Meta Store Session for {}", realmContext.getRealmIdentifier());
112+
emf = createEntityManagerFactory(realmContext, confFile, persistenceUnitName);
106113

107114
// init store
108115
this.store = store;
109116
this.storageIntegrationProvider = storageIntegrationProvider;
110117
}
111118

112-
/** Load the persistence unit properties from a given configuration file */
119+
/**
120+
* Create EntityManagerFactory.
121+
*
122+
* <p>The EntityManagerFactory creation is expensive, so we are caching and reusing it for each
123+
* realm.
124+
*/
113125
private EntityManagerFactory createEntityManagerFactory(
114126
@NotNull RealmContext realmContext,
115127
@Nullable String confFile,
116128
@Nullable String persistenceUnitName) {
129+
String realm = realmContext.getRealmIdentifier();
130+
EntityManagerFactory factory = realmFactories.getOrDefault(realm, null);
131+
if (factory != null) {
132+
return factory;
133+
}
134+
117135
ClassLoader prevClassLoader = Thread.currentThread().getContextClassLoader();
118136
try {
119137
persistenceUnitName = persistenceUnitName == null ? "polaris" : persistenceUnitName;
@@ -131,31 +149,44 @@ private EntityManagerFactory createEntityManagerFactory(
131149
if (prefixUrl == null) {
132150
prefixUrl = new File(jarPrefixPath).toURI().toURL();
133151
}
152+
153+
LOG.info(
154+
"Created a new ClassLoader with the jar {} in classpath to load the config file",
155+
prefixUrl);
156+
134157
URLClassLoader currentClassLoader =
135158
new URLClassLoader(new URL[] {prefixUrl}, this.getClass().getClassLoader());
159+
160+
LOG.debug("Update ClassLoader in current thread temporarily");
136161
Thread.currentThread().setContextClassLoader(currentClassLoader);
137162
}
138163

139164
Map<String, String> properties = loadProperties(confFile, persistenceUnitName);
140165
// Replace database name in JDBC URL with realm
141166
if (properties.containsKey(JDBC_URL)) {
142-
properties.put(
143-
JDBC_URL,
144-
properties.get(JDBC_URL).replace("{realm}", realmContext.getRealmIdentifier()));
167+
properties.put(JDBC_URL, properties.get(JDBC_URL).replace("{realm}", realm));
145168
}
146169
properties.put(ECLIPSELINK_PERSISTENCE_XML, confFile);
147170

148-
return Persistence.createEntityManagerFactory(persistenceUnitName, properties);
149-
} catch (Exception e) {
171+
factory = Persistence.createEntityManagerFactory(persistenceUnitName, properties);
172+
realmFactories.putIfAbsent(realm, factory);
173+
174+
return factory;
175+
} catch (IOException e) {
150176
throw new RuntimeException(e);
151177
} finally {
152178
Thread.currentThread().setContextClassLoader(prevClassLoader);
153179
}
154180
}
155181

182+
@TestOnly
183+
static void clearEntityManagerFactories() {
184+
realmFactories.clear();
185+
}
186+
156187
/** Load the persistence unit properties from a given configuration file */
157188
private Map<String, String> loadProperties(
158-
@NotNull String confFile, @NotNull String persistenceUnitName) throws Exception {
189+
@NotNull String confFile, @NotNull String persistenceUnitName) throws IOException {
159190
try {
160191
InputStream input =
161192
Thread.currentThread().getContextClassLoader().getResourceAsStream(confFile);
@@ -176,13 +207,16 @@ private Map<String, String> loadProperties(
176207
}
177208

178209
return properties;
179-
} catch (SAXException | IOException e) {
210+
} catch (XPathExpressionException
211+
| ParserConfigurationException
212+
| SAXException
213+
| IOException e) {
180214
String str =
181215
String.format(
182216
"Cannot find or parse the configuration file %s for persistence-unit %s",
183217
confFile, persistenceUnitName);
184-
LOG.error(str);
185-
throw new Exception(str);
218+
LOG.error(str, e);
219+
throw new IOException(str);
186220
}
187221
}
188222

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16-
package com.snowflake.polaris.persistence.impl.eclipselink;
16+
package io.polaris.extension.persistence.impl.eclipselink;
1717

1818
import static org.junit.jupiter.api.Assertions.assertFalse;
1919
import static org.junit.jupiter.api.Assertions.assertNotNull;
@@ -26,8 +26,6 @@
2626
import io.polaris.core.persistence.PolarisMetaStoreManagerImpl;
2727
import io.polaris.core.persistence.PolarisMetaStoreManagerTest;
2828
import io.polaris.core.persistence.PolarisTestMetaStoreManager;
29-
import io.polaris.extension.persistence.impl.eclipselink.PolarisEclipseLinkMetaStoreSessionImpl;
30-
import io.polaris.extension.persistence.impl.eclipselink.PolarisEclipseLinkStore;
3129
import java.time.ZoneId;
3230
import java.util.stream.Stream;
3331
import org.junit.jupiter.api.extension.ExtensionContext;
@@ -63,6 +61,9 @@ protected PolarisTestMetaStoreManager createPolarisTestMetaStoreManager() {
6361
@ParameterizedTest()
6462
@ArgumentsSource(CreateStoreSessionArgs.class)
6563
void testCreateStoreSession(String confFile, boolean success) {
64+
// Clear cache to prevent reuse EntityManagerFactory
65+
PolarisEclipseLinkMetaStoreSessionImpl.clearEntityManagerFactories();
66+
6667
PolarisDiagnostics diagServices = new PolarisDefaultDiagServiceImpl();
6768
PolarisEclipseLinkStore store = new PolarisEclipseLinkStore(diagServices);
6869
try {

0 commit comments

Comments
 (0)