diff --git a/airsonic-main/src/main/java/org/airsonic/player/controller/MusicFolderSettingsController.java b/airsonic-main/src/main/java/org/airsonic/player/controller/MusicFolderSettingsController.java index 13f948b7..1895e0a6 100644 --- a/airsonic-main/src/main/java/org/airsonic/player/controller/MusicFolderSettingsController.java +++ b/airsonic-main/src/main/java/org/airsonic/player/controller/MusicFolderSettingsController.java @@ -26,6 +26,8 @@ import org.airsonic.player.dao.MediaFileDao; import org.airsonic.player.domain.MusicFolder; import org.airsonic.player.service.MediaScannerService; import org.airsonic.player.service.SettingsService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; @@ -48,6 +50,8 @@ import java.util.stream.Collectors; @RequestMapping("/musicFolderSettings") public class MusicFolderSettingsController { + private static final Logger LOG = LoggerFactory.getLogger(MusicFolderSettingsController.class); + @Autowired private SettingsService settingsService; @Autowired @@ -93,9 +97,15 @@ public class MusicFolderSettingsController { private void expunge() { + LOG.info("Cleaning database..."); + LOG.info("Deleting non-present artists..."); artistDao.expunge(); + LOG.info("Deleting non-present albums..."); albumDao.expunge(); + LOG.info("Deleting non-present media files..."); mediaFileDao.expunge(); + LOG.debug("Database cleanup complete."); + mediaFileDao.checkpoint(); } private List wrap(List musicFolders) { diff --git a/airsonic-main/src/main/java/org/airsonic/player/dao/AbstractDao.java b/airsonic-main/src/main/java/org/airsonic/player/dao/AbstractDao.java index 107bff9b..757accc5 100644 --- a/airsonic-main/src/main/java/org/airsonic/player/dao/AbstractDao.java +++ b/airsonic-main/src/main/java/org/airsonic/player/dao/AbstractDao.java @@ -189,4 +189,6 @@ public class AbstractDao { this.daoHelper = daoHelper; } + public void checkpoint() { daoHelper.checkpoint(); } + } diff --git a/airsonic-main/src/main/java/org/airsonic/player/dao/DaoHelper.java b/airsonic-main/src/main/java/org/airsonic/player/dao/DaoHelper.java index a0e5b4b8..28acc180 100644 --- a/airsonic-main/src/main/java/org/airsonic/player/dao/DaoHelper.java +++ b/airsonic-main/src/main/java/org/airsonic/player/dao/DaoHelper.java @@ -46,4 +46,12 @@ public interface DaoHelper { NamedParameterJdbcTemplate getNamedParameterJdbcTemplate(); DataSource getDataSource(); + + /** + * Tries to perform a checkpoint against the database, if supported + * + * Database checkpoints will make sure that the database is written on the disk + * and optimize on-disk storage. + */ + public default void checkpoint() { } } diff --git a/airsonic-main/src/main/java/org/airsonic/player/dao/GenericDaoHelper.java b/airsonic-main/src/main/java/org/airsonic/player/dao/GenericDaoHelper.java index 792df5e0..32528607 100644 --- a/airsonic-main/src/main/java/org/airsonic/player/dao/GenericDaoHelper.java +++ b/airsonic-main/src/main/java/org/airsonic/player/dao/GenericDaoHelper.java @@ -1,5 +1,7 @@ package org.airsonic.player.dao; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; @@ -7,6 +9,8 @@ import javax.sql.DataSource; public class GenericDaoHelper implements DaoHelper { + private static final Logger LOG = LoggerFactory.getLogger(GenericDaoHelper.class); + final JdbcTemplate jdbcTemplate; final NamedParameterJdbcTemplate namedParameterJdbcTemplate; diff --git a/airsonic-main/src/main/java/org/airsonic/player/dao/LegacyHsqlDaoHelper.java b/airsonic-main/src/main/java/org/airsonic/player/dao/LegacyHsqlDaoHelper.java new file mode 100644 index 00000000..e6648c23 --- /dev/null +++ b/airsonic-main/src/main/java/org/airsonic/player/dao/LegacyHsqlDaoHelper.java @@ -0,0 +1,59 @@ +package org.airsonic.player.dao; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.datasource.DataSourceUtils; + +import javax.annotation.PreDestroy; +import javax.sql.DataSource; + +import java.sql.Connection; +import java.sql.SQLException; + +/** + * Special Dao Helper with additional features for managing the legacy embedded HSQL database. + */ +public class LegacyHsqlDaoHelper extends GenericDaoHelper { + + private static final Logger LOG = LoggerFactory.getLogger(LegacyHsqlDaoHelper.class); + + public LegacyHsqlDaoHelper(DataSource dataSource) { + super(dataSource); + } + + @Override + public void checkpoint() { + // HSQLDB (at least version 1) does not handle automatic checkpoints very well by default. + // This makes sure the temporary log is actually written to more persistent storage. + LOG.debug("Database checkpoint in progress..."); + getJdbcTemplate().execute("CHECKPOINT DEFRAG"); + LOG.debug("Database checkpoint complete."); + } + + @PreDestroy + public void onDestroy() { + Connection conn = null; + try { + // Properly shutdown the embedded HSQLDB database. + LOG.debug("Database shutdown in progress..."); + JdbcTemplate jdbcTemplate = getJdbcTemplate(); + conn = DataSourceUtils.getConnection(jdbcTemplate.getDataSource()); + conn.setAutoCommit(true); + jdbcTemplate.execute("SHUTDOWN"); + LOG.debug("Database shutdown complete."); + + } catch (SQLException e) { + LOG.error("Database shutdown failed: " + e); + e.printStackTrace(); + + } finally { + try { + if(conn != null) + conn.close(); + } catch(Exception ex) { + ex.printStackTrace(); + } + } + } +} diff --git a/airsonic-main/src/main/java/org/airsonic/player/service/MediaScannerService.java b/airsonic-main/src/main/java/org/airsonic/player/service/MediaScannerService.java index 427346b5..b4f6ee96 100644 --- a/airsonic-main/src/main/java/org/airsonic/player/service/MediaScannerService.java +++ b/airsonic-main/src/main/java/org/airsonic/player/service/MediaScannerService.java @@ -156,6 +156,7 @@ public class MediaScannerService { public void run() { doScanLibrary(); playlistService.importPlaylists(); + mediaFileDao.checkpoint(); } }; diff --git a/airsonic-main/src/main/resources/applicationContext-db-legacy.xml b/airsonic-main/src/main/resources/applicationContext-db-legacy.xml index f866f385..b933f284 100644 --- a/airsonic-main/src/main/resources/applicationContext-db-legacy.xml +++ b/airsonic-main/src/main/resources/applicationContext-db-legacy.xml @@ -6,11 +6,17 @@ - - \ No newline at end of file + + + + + + + + diff --git a/airsonic-main/src/main/resources/applicationContext-db.xml b/airsonic-main/src/main/resources/applicationContext-db.xml index c3e13b7c..de2db1ae 100644 --- a/airsonic-main/src/main/resources/applicationContext-db.xml +++ b/airsonic-main/src/main/resources/applicationContext-db.xml @@ -3,12 +3,6 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> - - - - - - @@ -17,6 +11,12 @@ + + + + + + @@ -38,4 +38,4 @@ - \ No newline at end of file + diff --git a/airsonic-main/src/main/resources/liquibase/10.2/setup-hsqldb-checkpoint-defrag.xml b/airsonic-main/src/main/resources/liquibase/10.2/setup-hsqldb-checkpoint-defrag.xml index 7f42743b..e62b2f71 100644 --- a/airsonic-main/src/main/resources/liquibase/10.2/setup-hsqldb-checkpoint-defrag.xml +++ b/airsonic-main/src/main/resources/liquibase/10.2/setup-hsqldb-checkpoint-defrag.xml @@ -2,10 +2,40 @@ xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd"> + + + + + + + + + + + CHECKPOINT DEFRAG; + Defragment the database before enabling auto defrag, so that the biggest part of the work is done during migration. + + + SET FILES LOG SIZE 64; + Automatically run a CHECKPOINT when the log is above 64MB. http://hsqldb.org/doc/guide/management-chapt.html#N150AB + + + SET FILES DEFRAG 50; + Sets the threshold in percentage for performing a DEFRAG during a checkpoint. http://hsqldb.org/doc/guide/management-chapt.html#N1506D + + + + + + 7:890e52428bbc2a792fea1e026a4b7610 + + + + @@ -18,7 +48,7 @@ SET CHECKPOINT DEFRAG 32; - Automatically defragment on CHECKPOINT when the wasted space is above 32MB. + Sets the threshold in percentage for performing a DEFRAG during a checkpoint.