From c0f36060912afe5dde3e7abd7e1d811df491914a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois-Xavier=20Thomas?= Date: Fri, 8 Mar 2019 22:08:51 +0100 Subject: [PATCH] Force database checkpoints on 'Clean-up' and 'Scan' actions This will only affect the (embedded/legacy) HSQLDB driver. Even though cff97ea9 should prevent the db log from getting uncontrollably large, the 'Clean-up database' and 'Scan' actions will additionally force a checkpoint to ensure this happens on big operations. --- .../MusicFolderSettingsController.java | 1 + .../org/airsonic/player/dao/AbstractDao.java | 2 + .../org/airsonic/player/dao/DaoHelper.java | 10 +++++ .../airsonic/player/dao/GenericDaoHelper.java | 39 +++++++++++++++++++ .../player/service/MediaScannerService.java | 1 + 5 files changed, 53 insertions(+) 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..b5a657d9 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 @@ -96,6 +96,7 @@ public class MusicFolderSettingsController { artistDao.expunge(); albumDao.expunge(); mediaFileDao.expunge(); + 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..a01836ab 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 boolean checkpoint() { return 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..5a5c2a84 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,14 @@ 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. + * + * @return true if the checkpoint succeeded + */ + public boolean 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..513ee03f 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,12 +1,20 @@ 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; +import org.springframework.jdbc.datasource.DataSourceUtils; import javax.sql.DataSource; +import java.sql.Connection; +import java.sql.DatabaseMetaData; + public class GenericDaoHelper implements DaoHelper { + private static final Logger LOG = LoggerFactory.getLogger(GenericDaoHelper.class); + final JdbcTemplate jdbcTemplate; final NamedParameterJdbcTemplate namedParameterJdbcTemplate; @@ -35,4 +43,35 @@ public class GenericDaoHelper implements DaoHelper { public DataSource getDataSource() { return dataSource; } + + @Override + public boolean checkpoint() { + + try { + Connection conn = DataSourceUtils.getConnection(getJdbcTemplate().getDataSource()); + DatabaseMetaData meta = conn.getMetaData(); + String productName = meta.getDatabaseProductName(); + int productVersion = meta.getDatabaseMajorVersion(); + + // 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. + if (productName.equals("HSQL Database Engine") && (productVersion == 1 || productVersion == 2)) { + LOG.info("Performing database checkpoint"); + getJdbcTemplate().execute("CHECKPOINT DEFRAG"); + return true; + } else { + LOG.debug("Database checkpoint not implemented for '" + productName + "'"); + return false; + } + } + + // Since this method is a best-effort operation, we don't want to show + // a message if the checkpoint failed ; just assume the operation is + // unsupported. + catch(java.sql.SQLException e) { + LOG.debug("An exception occurred during database checkpoint: " + e.toString()); + } + + return false; + } } 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(); } };