mirror of
https://github.com/scm-manager/scm-manager.git
synced 2026-05-07 08:45:34 +02:00
Introduce HikariCP as connection pool
Squash commits of branch feature/hikari: - Introduce HikariCP as connection pool - Log change - Close maintenance stores correctly - Do not force minimum number of connections - Log change - Assert stores are closed correctly ... and test QueryableStoreDeletionHandler - Fix license - Fix unit tests - Use constants - Change change log - Move hikari version to libraries - Enhance documentation - Use configuration for pool settings
This commit is contained in:
@@ -214,22 +214,37 @@ webapp:
|
||||
workingCopyPoolStrategy: sonia.scm.repository.work.SimpleCachingWorkingCopyPool
|
||||
## Amount of "cached" working copies
|
||||
workingCopyPoolSize: 5
|
||||
## Settings for queryable stores, which are backed by an SQLite database.
|
||||
## Timeouts and lifetimes are in seconds
|
||||
queryableStores:
|
||||
maxPoolSize: 10
|
||||
connectionTimeout: 30
|
||||
idleTimeout: 600
|
||||
maxLifetime: 1800
|
||||
leakDetectionThreshold: 30
|
||||
|
||||
|
||||
```
|
||||
|
||||
**Environment variables**
|
||||
|
||||
| Environment Variable | Corresponding config.yml property | Example |
|
||||
| ----------------------------------- | --------------------------------- | ------------------------------------------------------------------------------------------------ |
|
||||
| SCM_WEBAPP_WORKDIR | webapp.workDir | export SCM_WEBAPP_WORKDIR=/tmp/scm-work |
|
||||
| SCM_WEBAPP_HOMEDIR | webapp.homeDir | export SCM_WEBAPP_HOMEDIR=/var/lib/scm |
|
||||
| SCM_WEBAPP_CACHE_DATAFILE_ENABLED | webapp.cache.datafile.enabled | export SCM_WEBAPP_CACHE_DATAFILE_ENABLED=true |
|
||||
| SCM_WEBAPP_CACHE_STORE_ENABLED | webapp.cache.store.enabled | export SCM_WEBAPP_CACHE_STORE_ENABLED=true |
|
||||
| SCM_WEBAPP_ENDLESSJWT | webapp.endlessJwt | export SCM_WEBAPP_ENDLESSJWT=false |
|
||||
| SCM_WEBAPP_ASYNCTHREADS | webapp.asyncThreads | export SCM_WEBAPP_ASYNCTHREADS=4 |
|
||||
| SCM_WEBAPP_MAXASYNCABORTSECONDS | webapp.maxAsyncAbortSeconds | export SCM_WEBAPP_MAXASYNCABORTSECONDS=60 |
|
||||
| SCM_WEBAPP_CENTRALWORKQUEUE_WORKERS | webapp.centralWorkQueue.workers | export SCM_WEBAPP_CENTRALWORKQUEUE_WORKERS=4 |
|
||||
| SCM_WEBAPP_WORKINGCOPYPOOLSTRATEGY | webapp.workingCopyPoolStrategy | export SCM_WEBAPP_WORKINGCOPYPOOLSTRATEGY=sonia.scm.repository.work.SimpleCachingWorkingCopyPool |
|
||||
| SCM_WEBAPP_WORKINGCOPYPOOLSIZE | webapp.workingCopyPoolSize | export SCM_WEBAPP_WORKINGCOPYPOOLSIZE=5 |
|
||||
| SCM_WEBAPP_INITIALUSER | webapp.initialUser | export SCM_WEBAPP_INITIALUSER=scmadmin |
|
||||
| SCM_WEBAPP_INITIALPASSWORD | webapp.initialPassword | export SCM_WEBAPP_INITIALPASSWORD=scmadmin |
|
||||
| SCM_WEBAPP_SKIPADMINCREATION | webapp.skipAdminCreation | export SCM_WEBAPP_SKIPADMINCREATION=true |
|
||||
| Environment Variable | Corresponding config.yml property | Example |
|
||||
|---------------------------------------------------|-----------------------------------------------|--------------------------------------------------------------------------------------------------|
|
||||
| SCM_WEBAPP_WORKDIR | webapp.workDir | export SCM_WEBAPP_WORKDIR=/tmp/scm-work |
|
||||
| SCM_WEBAPP_HOMEDIR | webapp.homeDir | export SCM_WEBAPP_HOMEDIR=/var/lib/scm |
|
||||
| SCM_WEBAPP_CACHE_DATAFILE_ENABLED | webapp.cache.datafile.enabled | export SCM_WEBAPP_CACHE_DATAFILE_ENABLED=true |
|
||||
| SCM_WEBAPP_CACHE_STORE_ENABLED | webapp.cache.store.enabled | export SCM_WEBAPP_CACHE_STORE_ENABLED=true |
|
||||
| SCM_WEBAPP_ENDLESSJWT | webapp.endlessJwt | export SCM_WEBAPP_ENDLESSJWT=false |
|
||||
| SCM_WEBAPP_ASYNCTHREADS | webapp.asyncThreads | export SCM_WEBAPP_ASYNCTHREADS=4 |
|
||||
| SCM_WEBAPP_MAXASYNCABORTSECONDS | webapp.maxAsyncAbortSeconds | export SCM_WEBAPP_MAXASYNCABORTSECONDS=60 |
|
||||
| SCM_WEBAPP_CENTRALWORKQUEUE_WORKERS | webapp.centralWorkQueue.workers | export SCM_WEBAPP_CENTRALWORKQUEUE_WORKERS=4 |
|
||||
| SCM_WEBAPP_WORKINGCOPYPOOLSTRATEGY | webapp.workingCopyPoolStrategy | export SCM_WEBAPP_WORKINGCOPYPOOLSTRATEGY=sonia.scm.repository.work.SimpleCachingWorkingCopyPool |
|
||||
| SCM_WEBAPP_WORKINGCOPYPOOLSIZE | webapp.workingCopyPoolSize | export SCM_WEBAPP_WORKINGCOPYPOOLSIZE=5 |
|
||||
| SCM_WEBAPP_INITIALUSER | webapp.initialUser | export SCM_WEBAPP_INITIALUSER=scmadmin |
|
||||
| SCM_WEBAPP_INITIALPASSWORD | webapp.initialPassword | export SCM_WEBAPP_INITIALPASSWORD=scmadmin |
|
||||
| SCM_WEBAPP_SKIPADMINCREATION | webapp.skipAdminCreation | export SCM_WEBAPP_SKIPADMINCREATION=true |
|
||||
| SCM_WEBAPP_QUERYABLESTORES_MAXPOOLSIZE | webapp.queryableStores.maxPoolSize | export SCM_WEBAPP_QUERYABLESTORES_MAXPOOLSIZE=20 |
|
||||
| SCM_WEBAPP_QUERYABLESTORES_CONNECTIONTIMEOUT | webapp.queryableStores.connectionTimeout | export SCM_WEBAPP_QUERYABLESTORES_CONNECTIONTIMEOUT=30 |
|
||||
| SCM_WEBAPP_QUERYABLESTORES_IDLETIMEOUT | webapp.queryableStores.idleTimeout | export SCM_WEBAPP_QUERYABLESTORES_IDLETIMEOUT=600 |
|
||||
| SCM_WEBAPP_QUERYABLESTORES_MAXLIFETIME | webapp.queryableStores.maxLifetime | export SCM_WEBAPP_QUERYABLESTORES_MAXLIFETIME=1800 |
|
||||
| SCM_WEBAPP_QUERYABLESTORES_LEAKDETECTIONTHRESHOLD | webapp.queryableStores.leakDetectionThreshold | export SCM_WEBAPP_QUERYABLESTORES_LEAKDETECTIONTHRESHOLD=1800 |
|
||||
|
||||
@@ -78,6 +78,13 @@ however, specific methods are created here based on the parent classes mentioned
|
||||
To create and change data, specific IDs must be specified to access the store if parent classes have been defined. This
|
||||
store implements the known Store API (the `DataStore` interface), so no adjustments are needed in the application.
|
||||
|
||||
Since the new persistence layer is based on SQL, the stored have to be closed after use. This is done best with
|
||||
a try-with-resources block. If the store is not closed, the connection to the database will not be released,
|
||||
which can lead to performance issues and memory leaks that will let the application crash in the long run.
|
||||
If stores are not closed, the application will log a warning message. The best way to avoid this is to use
|
||||
unit tests with the `QueryableStoreExtension`, which will assert that all stores are closed correctly in the tested
|
||||
code.
|
||||
|
||||
### Queryable Store
|
||||
|
||||
For more advanced queries that also extend beyond the boundaries of the parent classes, there is a new store with a new
|
||||
@@ -308,33 +315,38 @@ public class Demo {
|
||||
entity.setAge(age);
|
||||
entity.setTags(tags);
|
||||
|
||||
QueryableMutableStore<MyEntity> store = storeFactory.getMutable();
|
||||
return store.put(entity);
|
||||
try (QueryableMutableStore<MyEntity> store = storeFactory.getMutable()) {
|
||||
return store.put(entity);
|
||||
}
|
||||
}
|
||||
|
||||
public MyEntity readById(String id) {
|
||||
QueryableMutableStore<MyEntity> store = storeFactory.getMutable();
|
||||
return store.get(id);
|
||||
try (QueryableMutableStore<MyEntity> store = storeFactory.getMutable()) {
|
||||
return store.get(id);
|
||||
}
|
||||
}
|
||||
|
||||
public Collection<MyEntity> findByAge(int age) {
|
||||
QueryableStore<MyEntity> store = storeFactory.get();
|
||||
return store.query(MyEntityQueryFields.AGE.eq(age)).findAll();
|
||||
try (QueryableStore<MyEntity> store = storeFactory.get()) {
|
||||
return store.query(MyEntityQueryFields.AGE.eq(age)).findAll();
|
||||
}
|
||||
}
|
||||
|
||||
public Collection<MyEntity> findByName(String name) {
|
||||
QueryableStore<MyEntity> store = storeFactory.get();
|
||||
return store.query(
|
||||
Conditions.or(
|
||||
MyEntityQueryFields.NAME.eq(name),
|
||||
MyEntityQueryFields.ALIAS.eq(name)
|
||||
)
|
||||
).findAll();
|
||||
try (QueryableStore<MyEntity> store = storeFactory.get()) {
|
||||
return store.query(
|
||||
Conditions.or(
|
||||
MyEntityQueryFields.NAME.eq(name),
|
||||
MyEntityQueryFields.ALIAS.eq(name)
|
||||
)
|
||||
).findAll();
|
||||
}
|
||||
}
|
||||
|
||||
public Collection<MyEntity> findByTag(String tag) {
|
||||
QueryableStore<MyEntity> store = storeFactory.get();
|
||||
return store.query(MyEntityQueryFields.TAGS.contains(tag)).findAll();
|
||||
try (QueryableStore<MyEntity> store = storeFactory.get()) {
|
||||
return store.query(MyEntityQueryFields.TAGS.contains(tag)).findAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -364,22 +376,25 @@ public class Demo {
|
||||
}
|
||||
|
||||
public void addContact(User user, String mail) {
|
||||
QueryableMutableStore<Contact> store = storeFactory.getMutable(user);
|
||||
Contact contact = new Contact();
|
||||
contact.setMail(mail);
|
||||
store.put(contact);
|
||||
try (QueryableMutableStore<Contact> store = storeFactory.getMutable(user)) {
|
||||
store.put(contact);
|
||||
}
|
||||
}
|
||||
|
||||
/** Get contact for a single user. */
|
||||
public Collection<Contact> getContacts(User user) {
|
||||
QueryableMutableStore<Contact> store = storeFactory.getMutable(user);
|
||||
return store.getAll().values();
|
||||
try (QueryableMutableStore<Contact> store = storeFactory.getMutable(user)) {
|
||||
return store.getAll().values();
|
||||
}
|
||||
}
|
||||
|
||||
/** Get all contacts for all users. */
|
||||
public Collection<Contact> getAllContacts() {
|
||||
QueryableStore<Contact> store = storeFactory.getOverall();
|
||||
return store.query().findAll();
|
||||
try (QueryableStore<Contact> store = storeFactory.getOverall()) {
|
||||
return store.query().findAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -481,6 +496,7 @@ public class Contact {
|
||||
The following update step can be used to add the `type` field to all `Contact` entities:
|
||||
|
||||
```java
|
||||
|
||||
@Extension
|
||||
public class AddTypeToContactsUpdateStep implements UpdateStep {
|
||||
|
||||
@@ -493,8 +509,9 @@ public class AddTypeToContactsUpdateStep implements UpdateStep {
|
||||
|
||||
@Override
|
||||
public void doUpdate() {
|
||||
try (MaintenanceIterator<Contact> iter = updateStepUtilFactory.forQueryableType(Contact.class).iterateAll()) {
|
||||
while(iter.hasNext()) {
|
||||
try (sonia.scm.store.QueryableMaintenanceStore<Object> store = updateStepUtilFactory.forQueryableType(Contact.class);
|
||||
MaintenanceIterator<Contact> iter = store.iterateAll()) {
|
||||
while (iter.hasNext()) {
|
||||
MaintenanceStoreEntry<Contact> entry = iter.next();
|
||||
Contact contact = entry.get();
|
||||
contact.setType("personal");
|
||||
|
||||
Reference in New Issue
Block a user