All Downloads are FREE. Search and download functionalities are using the official Maven repository.

tech.jhipster.config.liquibase.AsyncSpringLiquibase Maven / Gradle / Ivy

/*
 * Copyright 2016-2023 the original author or authors from the JHipster project.
 *
 * This file is part of the JHipster project, see https://www.jhipster.tech/
 * for more information.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package tech.jhipster.config.liquibase;

import static tech.jhipster.config.JHipsterConstants.*;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.concurrent.Executor;
import liquibase.exception.LiquibaseException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.liquibase.DataSourceClosingSpringLiquibase;
import org.springframework.core.env.Environment;
import org.springframework.core.env.Profiles;
import org.springframework.util.StopWatch;

/**
 * Specific liquibase.integration.spring.SpringLiquibase that will update the database asynchronously and close
 * DataSource if necessary. 

By default, this asynchronous version only works when using the "dev" profile.

The standard * liquibase.integration.spring.SpringLiquibase starts Liquibase in the current thread:

  • This is needed if you * want to do some database requests at startup
  • This ensure that the database is ready when the application * starts
But as this is a rather slow process, we use this asynchronous version to speed up our start-up * time:
  • On a recent MacBook Pro, start-up time is down from 14 seconds to 8 seconds
  • In production, * this can help your application run on platforms like Heroku, where it must start/restart very quickly
*/ public class AsyncSpringLiquibase extends DataSourceClosingSpringLiquibase { /** Constant DISABLED_MESSAGE="Liquibase is disabled" */ public static final String DISABLED_MESSAGE = "Liquibase is disabled"; /** Constant STARTING_ASYNC_MESSAGE="Starting Liquibase asynchronously, your"{trunked} */ public static final String STARTING_ASYNC_MESSAGE = "Starting Liquibase asynchronously, your database might not be ready at startup!"; /** Constant STARTING_SYNC_MESSAGE="Starting Liquibase synchronously" */ public static final String STARTING_SYNC_MESSAGE = "Starting Liquibase synchronously"; /** Constant STARTED_MESSAGE="Liquibase has updated your database in "{trunked} */ public static final String STARTED_MESSAGE = "Liquibase has updated your database in {} ms"; /** Constant EXCEPTION_MESSAGE="Liquibase could not start correctly, yo"{trunked} */ public static final String EXCEPTION_MESSAGE = "Liquibase could not start correctly, your database is NOT ready: {}"; /** Constant SLOWNESS_THRESHOLD=5 */ public static final long SLOWNESS_THRESHOLD = 5; // seconds /** Constant SLOWNESS_MESSAGE="Warning, Liquibase took more than {} se"{trunked} */ public static final String SLOWNESS_MESSAGE = "Warning, Liquibase took more than {} seconds to start up!"; // named "logger" because there is already a field called "log" in "SpringLiquibase" private final Logger logger = LoggerFactory.getLogger(AsyncSpringLiquibase.class); private final Executor executor; private final Environment env; /** *

Constructor for AsyncSpringLiquibase.

* * @param executor a {@link java.util.concurrent.Executor} object. * @param env a {@link org.springframework.core.env.Environment} object. */ public AsyncSpringLiquibase(Executor executor, Environment env) { this.executor = executor; this.env = env; } /** {@inheritDoc} */ @Override public void afterPropertiesSet() throws LiquibaseException { if (isLiquibaseDisabled()) { logger.debug(DISABLED_MESSAGE); return; } if (isAsyncProfileActive()) { handleAsyncExecution(); } else { logger.debug(STARTING_SYNC_MESSAGE); initDb(); } } private boolean isLiquibaseDisabled() { return env.acceptsProfiles(Profiles.of(SPRING_PROFILE_NO_LIQUIBASE)); } private boolean isAsyncProfileActive() { return env.acceptsProfiles(Profiles.of(SPRING_PROFILE_DEVELOPMENT + "|" + SPRING_PROFILE_HEROKU)); } private void handleAsyncExecution() { // Prevent Thread Lock with spring-cloud-context GenericScope // https://github.com/spring-cloud/spring-cloud-commons/commit/aaa7288bae3bb4d6fdbef1041691223238d77b7b#diff-afa0715eafc2b0154475fe672dab70e4R328 try (Connection connection = getDataSource().getConnection()) { executor.execute(() -> { try { logger.warn(STARTING_ASYNC_MESSAGE); initDb(); } catch (LiquibaseException e) { logger.error(EXCEPTION_MESSAGE, e.getMessage(), e); } }); } catch (SQLException e) { logger.error(EXCEPTION_MESSAGE, e.getMessage(), e); } } /** *

initDb.

* * @throws liquibase.exception.LiquibaseException if any. */ protected void initDb() throws LiquibaseException { StopWatch watch = new StopWatch(); watch.start(); super.afterPropertiesSet(); watch.stop(); logger.debug(STARTED_MESSAGE, watch.getTotalTimeMillis()); boolean isExecutionTimeLong = watch.getTotalTimeMillis() > SLOWNESS_THRESHOLD * 1000L; if (isExecutionTimeLong) { logger.warn(SLOWNESS_MESSAGE, SLOWNESS_THRESHOLD); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy