----初始化项目

This commit is contained in:
2025-09-19 20:49:14 +08:00
parent b345d2828d
commit df7765c400
2867 changed files with 359313 additions and 89 deletions

74
persistence/pom.xml Normal file
View File

@ -0,0 +1,74 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 1999-2023 Alibaba Group Holding Ltd.
~
~ 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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-all</artifactId>
<version>${revision}</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>nacos-persistence</artifactId>
<name>nacos-persistence ${project.version}</name>
<url>https://nacos.io</url>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-datasource-plugin</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-sys</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-consistency</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-core</artifactId>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
</dependency>
<dependency>
<groupId>kingbase</groupId>
<artifactId>kingbase</artifactId>
</dependency>
<dependency>
<groupId>org.apache.derby</groupId>
<artifactId>derby</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,89 @@
/*
* Copyright 1999-2023 Alibaba Group Holding Ltd.
*
* 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 com.alibaba.nacos.persistence.configuration;
import com.alibaba.nacos.persistence.constants.PersistenceConstant;
import com.alibaba.nacos.persistence.utils.DatasourcePlatformUtil;
import com.alibaba.nacos.sys.env.EnvUtil;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
/**
* Configuration about datasource.
*
* @author xiweng.yy
*/
public class DatasourceConfiguration implements ApplicationContextInitializer<ConfigurableApplicationContext> {
/**
* Standalone mode uses DB.
*/
public static boolean useExternalDB = false;
/**
* Inline storage value = ${nacos.standalone}.
*/
public static boolean embeddedStorage = EnvUtil.getStandaloneMode();
public static boolean isUseExternalDB() {
return useExternalDB;
}
public static void setUseExternalDB(boolean useExternalDB) {
DatasourceConfiguration.useExternalDB = useExternalDB;
}
public static boolean isEmbeddedStorage() {
return embeddedStorage;
}
public static void setEmbeddedStorage(boolean embeddedStorage) {
DatasourceConfiguration.embeddedStorage = embeddedStorage;
}
private void loadDatasourceConfiguration() {
// External data sources are used by default in cluster mode
String platform = DatasourcePlatformUtil.getDatasourcePlatform("");
boolean useExternalStorage =
!PersistenceConstant.EMPTY_DATASOURCE_PLATFORM.equalsIgnoreCase(platform) && !PersistenceConstant.DERBY
.equalsIgnoreCase(platform);
setUseExternalDB(useExternalStorage);
// must initialize after setUseExternalDB
// This value is true in stand-alone mode and false in cluster mode
// If this value is set to true in cluster mode, nacos's distributed storage engine is turned on
// default value is depend on ${nacos.standalone}
if (isUseExternalDB()) {
setEmbeddedStorage(false);
} else {
boolean embeddedStorage = isEmbeddedStorage() || Boolean.getBoolean(PersistenceConstant.EMBEDDED_STORAGE);
setEmbeddedStorage(embeddedStorage);
// If the embedded data source storage is not turned on, it is automatically
// upgraded to the external data source storage, as before
if (!embeddedStorage) {
setUseExternalDB(true);
}
}
}
@Override
public void initialize(final ConfigurableApplicationContext applicationContext) {
loadDatasourceConfiguration();
}
}

View File

@ -0,0 +1,36 @@
/*
* Copyright 1999-2023 Alibaba Group Holding Ltd.
*
* 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 com.alibaba.nacos.persistence.configuration.condition;
import com.alibaba.nacos.persistence.configuration.DatasourceConfiguration;
import com.alibaba.nacos.sys.env.EnvUtil;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
/**
* when embeddedStorage==true and nacos.standalone=false
*
* @author <a href="mailto:liaochuntao@live.com">liaochuntao</a>
*/
public class ConditionDistributedEmbedStorage implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return DatasourceConfiguration.isEmbeddedStorage() && !EnvUtil.getStandaloneMode();
}
}

View File

@ -0,0 +1,35 @@
/*
* Copyright 1999-2023 Alibaba Group Holding Ltd.
*
* 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 com.alibaba.nacos.persistence.configuration.condition;
import com.alibaba.nacos.persistence.configuration.DatasourceConfiguration;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
/**
* Judge whether to user EmbeddedStorage by condition.
*
* @author <a href="mailto:liaochuntao@live.com">liaochuntao</a>
*/
public class ConditionOnEmbeddedStorage implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return DatasourceConfiguration.isEmbeddedStorage();
}
}

View File

@ -0,0 +1,36 @@
/*
* Copyright 1999-2023 Alibaba Group Holding Ltd.
*
* 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 com.alibaba.nacos.persistence.configuration.condition;
import com.alibaba.nacos.persistence.configuration.DatasourceConfiguration;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
/**
* Judge whether to user ExternalStorage by condition.
*
* @author <a href="mailto:liaochuntao@live.com">liaochuntao</a>
*/
public class ConditionOnExternalStorage implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return !DatasourceConfiguration.isEmbeddedStorage();
}
}

View File

@ -0,0 +1,37 @@
/*
* Copyright 1999-2023 Alibaba Group Holding Ltd.
*
* 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 com.alibaba.nacos.persistence.configuration.condition;
import com.alibaba.nacos.persistence.configuration.DatasourceConfiguration;
import com.alibaba.nacos.sys.env.EnvUtil;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
/**
* Judge whether to user StandaloneEmbedStorage by condition.
* When embeddedStorage==false.
*
* @author <a href="mailto:liaochuntao@live.com">liaochuntao</a>
*/
public class ConditionStandaloneEmbedStorage implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return DatasourceConfiguration.isEmbeddedStorage() && EnvUtil.getStandaloneMode();
}
}

View File

@ -0,0 +1,55 @@
/*
* Copyright 1999-2023 Alibaba Group Holding Ltd.
*
* 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 com.alibaba.nacos.persistence.constants;
/**
* Persistence constant.
*
* @author xiweng.yy
*/
public class PersistenceConstant {
public static final String DEFAULT_ENCODE = "UTF-8";
/**
* May be removed with the upgrade of springboot version.
*/
public static final String DATASOURCE_PLATFORM_PROPERTY_OLD = "spring.datasource.platform";
public static final String DATASOURCE_PLATFORM_PROPERTY = "spring.sql.init.platform";
public static final String MYSQL = "mysql";
public static final String DERBY = "derby";
public static final String EMPTY_DATASOURCE_PLATFORM = "";
public static final String EMBEDDED_STORAGE = "embeddedStorage";
/**
* The derby base dir.
*/
public static final String DERBY_BASE_DIR = "derby-data";
/**
* Specifies that reads wait without timeout.
*/
public static final String EXTEND_NEED_READ_UNTIL_HAVE_DATA = "00--0-read-join-0--00";
public static final String CONFIG_MODEL_RAFT_GROUP = "nacos_config";
}

View File

@ -0,0 +1,87 @@
/*
* Copyright 1999-2023 Alibaba Group Holding Ltd.
*
* 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 com.alibaba.nacos.persistence.datasource;
import com.zaxxer.hikari.HikariDataSource;
import org.springframework.boot.context.properties.bind.Bindable;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.core.env.Environment;
import java.util.concurrent.TimeUnit;
/**
* DataSource pool properties.
*
* <p>Nacos server use HikariCP as the datasource pool. So the basic pool properties will based on {@link
* com.zaxxer.hikari.HikariDataSource}.
*
* @author xiweng.yy
*/
public class DataSourcePoolProperties {
public static final long DEFAULT_CONNECTION_TIMEOUT = TimeUnit.SECONDS.toMillis(3L);
public static final long DEFAULT_VALIDATION_TIMEOUT = TimeUnit.SECONDS.toMillis(10L);
public static final long DEFAULT_IDLE_TIMEOUT = TimeUnit.MINUTES.toMillis(10L);
public static final int DEFAULT_MAX_POOL_SIZE = 20;
public static final int DEFAULT_MINIMUM_IDLE = 2;
private final HikariDataSource dataSource;
private DataSourcePoolProperties() {
dataSource = new HikariDataSource();
dataSource.setIdleTimeout(DEFAULT_IDLE_TIMEOUT);
dataSource.setConnectionTimeout(DEFAULT_CONNECTION_TIMEOUT);
dataSource.setValidationTimeout(DEFAULT_VALIDATION_TIMEOUT);
dataSource.setMaximumPoolSize(DEFAULT_MAX_POOL_SIZE);
dataSource.setMinimumIdle(DEFAULT_MINIMUM_IDLE);
}
/**
* Build new Hikari config.
*
* @return new hikari config
*/
public static DataSourcePoolProperties build(Environment environment) {
DataSourcePoolProperties result = new DataSourcePoolProperties();
Binder.get(environment).bind("db.pool.config", Bindable.ofInstance(result.getDataSource()));
return result;
}
public void setDriverClassName(final String driverClassName) {
dataSource.setDriverClassName(driverClassName);
}
public void setJdbcUrl(final String jdbcUrl) {
dataSource.setJdbcUrl(jdbcUrl);
}
public void setUsername(final String username) {
dataSource.setUsername(username);
}
public void setPassword(final String password) {
dataSource.setPassword(password);
}
public HikariDataSource getDataSource() {
return dataSource;
}
}

View File

@ -0,0 +1,87 @@
/*
* Copyright 1999-2023 Alibaba Group Holding Ltd.
*
* 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 com.alibaba.nacos.persistence.datasource;
import java.io.IOException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.transaction.support.TransactionTemplate;
/**
* Datasource interface.
*
* @author Nacos
*/
public interface DataSourceService {
/**
* Initialize the relevant resource information.
*
* @throws Exception exception.
*/
void init() throws Exception;
/**
* Reload.
*
* @throws IOException exception.
*/
void reload() throws IOException;
/**
* Check master db.
*
* @return is master.
*/
boolean checkMasterWritable();
/**
* Get jdbc template.
*
* @return JdbcTemplate.
*/
JdbcTemplate getJdbcTemplate();
/**
* Get transaction template.
*
* @return TransactionTemplate.
*/
TransactionTemplate getTransactionTemplate();
/**
* Get current db url.
*
* @return database url
*/
String getCurrentDbUrl();
/**
* Get heath information.
*
* @return heath info.
*/
String getHealth();
/**
* Get current db type.
*
* @return
*/
String getDataSourceType();
}

View File

@ -0,0 +1,64 @@
/*
* Copyright 1999-2023 Alibaba Group Holding Ltd.
*
* 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 com.alibaba.nacos.persistence.datasource;
import com.alibaba.nacos.persistence.configuration.DatasourceConfiguration;
/**
* Datasource adapter.
*
* @author Nacos
*/
public class DynamicDataSource {
private DataSourceService localDataSourceService = null;
private DataSourceService basicDataSourceService = null;
private static final DynamicDataSource INSTANCE = new DynamicDataSource();
private DynamicDataSource() {}
public static DynamicDataSource getInstance() {
return INSTANCE;
}
public synchronized DataSourceService getDataSource() {
try {
// Embedded storage is used by default in stand-alone mode
// In cluster mode, external databases are used by default
if (DatasourceConfiguration.isEmbeddedStorage()) {
if (localDataSourceService == null) {
localDataSourceService = new LocalDataSourceServiceImpl();
localDataSourceService.init();
}
return localDataSourceService;
} else {
if (basicDataSourceService == null) {
basicDataSourceService = new ExternalDataSourceServiceImpl();
basicDataSourceService.init();
}
return basicDataSourceService;
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}

View File

@ -0,0 +1,114 @@
package com.alibaba.nacos.persistence.datasource;
import com.alibaba.nacos.common.utils.Preconditions;
import com.alibaba.nacos.common.utils.StringUtils;
import com.zaxxer.hikari.HikariDataSource;
import org.springframework.boot.context.properties.bind.Bindable;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.core.env.Environment;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import static com.alibaba.nacos.common.utils.CollectionUtils.getOrDefault;
/**
* Properties of external DataSource.
*
* @author Nacos
*/
public class ExternalDataSourceProperties {
private static final String JDBC_DRIVER_NAME = "com.mysql.cj.jdbc.Driver";
private static final String TEST_QUERY = "SELECT 1";
private Integer num;
private List<String> url = new ArrayList<>();
private List<String> user = new ArrayList<>();
private List<String> password = new ArrayList<>();
private List<String> driverClassName = new ArrayList<>();
private List<String> testQuery = new ArrayList<>();
public void setNum(Integer num) {
this.num = num;
}
public void setUrl(List<String> url) {
this.url = url;
}
public void setUser(List<String> user) {
this.user = user;
}
public void setPassword(List<String> password) {
this.password = password;
}
public List<String> getDriverClassName() {
return driverClassName;
}
public void setDriverClassName(List<String> driverClassName) {
this.driverClassName = driverClassName;
}
public void setTestQuery(List<String> testQuery) {
this.testQuery = testQuery;
}
/**
* Build serveral HikariDataSource.
*
* @param environment {@link Environment}
* @param callback Callback function when constructing data source
* @return List of {@link HikariDataSource}
*/
List<HikariDataSource> build(Environment environment, Callback<HikariDataSource> callback) {
List<HikariDataSource> dataSources = new ArrayList<>();
Binder.get(environment).bind("db", Bindable.ofInstance(this));
Preconditions.checkArgument(Objects.nonNull(num), "db.num is null");
Preconditions.checkArgument(isNotEmpty(user), "db.user or db.user.[index] is null");
Preconditions.checkArgument(isNotEmpty(password), "db.password or db.password.[index] is null");
Preconditions.checkArgument(isNotEmpty(driverClassName), "db.driverClassName or db.driverClassName.[index] is null");
for (int index = 0; index < num; index++) {
int currentSize = index + 1;
Preconditions.checkArgument(url.size() >= currentSize, "db.url.%s is null", index);
DataSourcePoolProperties poolProperties = DataSourcePoolProperties.build(environment);
poolProperties.setDriverClassName(getOrDefault(driverClassName, index, JDBC_DRIVER_NAME).trim());
poolProperties.setJdbcUrl(url.get(index).trim());
poolProperties.setUsername(getOrDefault(user, index, user.get(0)).trim());
poolProperties.setPassword(getOrDefault(password, index, password.get(0)).trim());
HikariDataSource ds = poolProperties.getDataSource();
if (StringUtils.isEmpty(ds.getConnectionTestQuery())) {
ds.setConnectionTestQuery(getOrDefault(testQuery, index, TEST_QUERY).trim());
}
dataSources.add(ds);
callback.accept(ds);
}
Preconditions.checkArgument(isNotEmpty(dataSources), "no datasource available");
return dataSources;
}
public <E>boolean isNotEmpty(Collection<E> col) {
return col!=null && !col.isEmpty();
}
interface Callback<D> {
/**
* Perform custom logic.
*
* @param datasource dataSource.
*/
void accept(D datasource);
}
}

View File

@ -0,0 +1,306 @@
/*
* Copyright 1999-2023 Alibaba Group Holding Ltd.
*
* 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 com.alibaba.nacos.persistence.datasource;
import com.alibaba.nacos.common.utils.ConvertUtils;
import com.alibaba.nacos.common.utils.InternetAddressUtil;
import com.alibaba.nacos.common.utils.StringUtils;
import com.alibaba.nacos.persistence.configuration.DatasourceConfiguration;
import com.alibaba.nacos.persistence.monitor.DatasourceMetrics;
import com.alibaba.nacos.persistence.utils.ConnectionCheckUtil;
import com.alibaba.nacos.persistence.utils.DatasourcePlatformUtil;
import com.alibaba.nacos.persistence.utils.PersistenceExecutor;
import com.alibaba.nacos.sys.env.EnvUtil;
import com.zaxxer.hikari.HikariDataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.CannotGetJdbcConnectionException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.support.TransactionTemplate;
import javax.sql.DataSource;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* Base data source.
*
* @author Nacos
*/
public class ExternalDataSourceServiceImpl implements DataSourceService {
private static final Logger LOGGER = LoggerFactory.getLogger(ExternalDataSourceServiceImpl.class);
/**
* JDBC execute timeout value, unit:second.
*/
private int queryTimeout = 3;
private static final int TRANSACTION_QUERY_TIMEOUT = 5;
private static final int DB_MASTER_SELECT_THRESHOLD = 1;
private static final String DB_LOAD_ERROR_MSG = "[db-load-error]load jdbc.properties error";
private List<HikariDataSource> dataSourceList = new ArrayList<>();
private JdbcTemplate jt;
private DataSourceTransactionManager tm;
private TransactionTemplate tjt;
private JdbcTemplate testMasterJT;
private JdbcTemplate testMasterWritableJT;
private volatile List<JdbcTemplate> testJtList;
private volatile List<Boolean> isHealthList;
private volatile int masterIndex;
private String dataSourceType = "";
private final String defaultDataSourceType = "";
@Override
public void init() {
queryTimeout = ConvertUtils.toInt(System.getProperty("QUERYTIMEOUT"), 3);
jt = new JdbcTemplate();
// Set the maximum number of records to prevent memory expansion
jt.setMaxRows(50000);
jt.setQueryTimeout(queryTimeout);
testMasterJT = new JdbcTemplate();
testMasterJT.setQueryTimeout(queryTimeout);
testMasterWritableJT = new JdbcTemplate();
// Prevent the login interface from being too long because the main library is not available
testMasterWritableJT.setQueryTimeout(1);
// Database health check
testJtList = new ArrayList<>();
isHealthList = new ArrayList<>();
tm = new DataSourceTransactionManager();
tjt = new TransactionTemplate(tm);
// Transaction timeout needs to be distinguished from ordinary operations.
tjt.setTimeout(120);
dataSourceType = DatasourcePlatformUtil.getDatasourcePlatform(defaultDataSourceType);
if (DatasourceConfiguration.isUseExternalDB()) {
try {
reload();
} catch (IOException e) {
LOGGER.error("[ExternalDataSourceService] datasource reload error", e);
throw new RuntimeException(DB_LOAD_ERROR_MSG, e);
}
if (this.dataSourceList.size() > DB_MASTER_SELECT_THRESHOLD) {
PersistenceExecutor.scheduleTask(new SelectMasterTask(), 10, 10, TimeUnit.SECONDS);
}
PersistenceExecutor.scheduleTask(new CheckDbHealthTask(), 10, 10, TimeUnit.SECONDS);
}
}
@Override
public synchronized void reload() throws IOException {
try {
final List<JdbcTemplate> testJtListNew = new ArrayList<JdbcTemplate>();
final List<Boolean> isHealthListNew = new ArrayList<Boolean>();
List<HikariDataSource> dataSourceListNew = new ExternalDataSourceProperties()
.build(EnvUtil.getEnvironment(), (dataSource) -> {
//check datasource connection
ConnectionCheckUtil.checkDataSourceConnection(dataSource);
JdbcTemplate jdbcTemplate = new JdbcTemplate();
jdbcTemplate.setQueryTimeout(queryTimeout);
jdbcTemplate.setDataSource(dataSource);
testJtListNew.add(jdbcTemplate);
isHealthListNew.add(Boolean.TRUE);
});
final List<HikariDataSource> dataSourceListOld = dataSourceList;
final List<JdbcTemplate> testJtListOld = testJtList;
dataSourceList = dataSourceListNew;
testJtList = testJtListNew;
isHealthList = isHealthListNew;
new SelectMasterTask().run();
new CheckDbHealthTask().run();
//close old datasource.
if (dataSourceListOld != null && !dataSourceListOld.isEmpty()) {
for (HikariDataSource dataSource : dataSourceListOld) {
dataSource.close();
}
}
if (testJtListOld != null && !testJtListOld.isEmpty()) {
for (JdbcTemplate oldJdbc : testJtListOld) {
oldJdbc.setDataSource(null);
}
}
} catch (RuntimeException e) {
LOGGER.error(DB_LOAD_ERROR_MSG, e);
throw new IOException(e);
}
}
@Override
public boolean checkMasterWritable() {
testMasterWritableJT.setDataSource(jt.getDataSource());
// Prevent the login interface from being too long because the main library is not available
testMasterWritableJT.setQueryTimeout(1);
String sql = " SELECT @@read_only ";
try {
Integer result = testMasterWritableJT.queryForObject(sql, Integer.class);
if (result == null) {
return false;
} else {
return result == 0;
}
} catch (CannotGetJdbcConnectionException e) {
LOGGER.error("[db-error] " + e.toString(), e);
return false;
}
}
@Override
public JdbcTemplate getJdbcTemplate() {
return this.jt;
}
@Override
public TransactionTemplate getTransactionTemplate() {
return this.tjt;
}
@Override
public String getCurrentDbUrl() {
DataSource ds = this.jt.getDataSource();
if (ds == null) {
return StringUtils.EMPTY;
}
HikariDataSource bds = (HikariDataSource) ds;
return bds.getJdbcUrl();
}
@Override
public String getHealth() {
for (int i = 0; i < isHealthList.size(); i++) {
if (!isHealthList.get(i)) {
if (i == masterIndex) {
// The master is unhealthy.
return "DOWN:" + InternetAddressUtil.getIPFromString(dataSourceList.get(i).getJdbcUrl());
} else {
// The slave is unhealthy.
return "WARN:" + InternetAddressUtil.getIPFromString(dataSourceList.get(i).getJdbcUrl());
}
}
}
return "UP";
}
@Override
public String getDataSourceType() {
return dataSourceType;
}
class SelectMasterTask implements Runnable {
@Override
public void run() {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("check master db.");
}
boolean isFound = false;
int index = -1;
for (HikariDataSource ds : dataSourceList) {
index++;
testMasterJT.setDataSource(ds);
testMasterJT.setQueryTimeout(queryTimeout);
try {
testMasterJT.update("DELETE FROM config_info WHERE data_id='com.alibaba.nacos.testMasterDB'");
if (jt.getDataSource() != ds) {
LOGGER.warn("[master-db] {}", ds.getJdbcUrl());
}
jt.setDataSource(ds);
tm.setDataSource(ds);
isFound = true;
masterIndex = index;
break;
} catch (DataAccessException e) { // read only
LOGGER.warn("[master-db] master db access error", e);
}
}
if (!isFound) {
LOGGER.error("[master-db] master db not found.");
DatasourceMetrics.getDbException().increment();
}
}
}
@SuppressWarnings("PMD.ClassNamingShouldBeCamelRule")
class CheckDbHealthTask implements Runnable {
@Override
public void run() {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("check db health.");
}
String sql = "SELECT * FROM config_info_beta WHERE id = 1";
for (int i = 0; i < testJtList.size(); i++) {
JdbcTemplate jdbcTemplate = testJtList.get(i);
try {
try {
jdbcTemplate.queryForMap(sql);
} catch (EmptyResultDataAccessException e) {
// do nothing.
}
isHealthList.set(i, Boolean.TRUE);
} catch (DataAccessException e) {
if (i == masterIndex) {
LOGGER.error("[db-error] master db {} down.",
InternetAddressUtil.getIPFromString(dataSourceList.get(i).getJdbcUrl()));
} else {
LOGGER.error("[db-error] slave db {} down.",
InternetAddressUtil.getIPFromString(dataSourceList.get(i).getJdbcUrl()));
}
isHealthList.set(i, Boolean.FALSE);
DatasourceMetrics.getDbException().increment();
}
}
}
}
}

View File

@ -0,0 +1,270 @@
/*
* Copyright 1999-2023 Alibaba Group Holding Ltd.
*
* 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 com.alibaba.nacos.persistence.datasource;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.exception.runtime.NacosRuntimeException;
import com.alibaba.nacos.common.utils.IoUtils;
import com.alibaba.nacos.common.utils.StringUtils;
import com.alibaba.nacos.persistence.configuration.DatasourceConfiguration;
import com.alibaba.nacos.persistence.constants.PersistenceConstant;
import com.alibaba.nacos.sys.env.EnvUtil;
import com.alibaba.nacos.sys.utils.DiskUtils;
import com.zaxxer.hikari.HikariDataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.support.TransactionTemplate;
import javax.sql.DataSource;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.net.URL;
import java.nio.file.Paths;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
/**
* local data source.
*
* @author Nacos
*/
public class LocalDataSourceServiceImpl implements DataSourceService {
private static final Logger LOGGER = LoggerFactory.getLogger(LocalDataSourceServiceImpl.class);
private final String jdbcDriverName = "org.apache.derby.jdbc.EmbeddedDriver";
private final String userName = "nacos";
private final String password = "nacos";
private final String derbyBaseDir = "data" + File.separator + PersistenceConstant.DERBY_BASE_DIR;
private final String derbyShutdownErrMsg = "Derby system shutdown.";
private volatile JdbcTemplate jt;
private volatile TransactionTemplate tjt;
private boolean initialize = false;
private boolean jdbcTemplateInit = false;
private String healthStatus = "UP";
private String dataSourceType = "derby";
@Override
public synchronized void init() throws Exception {
if (DatasourceConfiguration.isUseExternalDB()) {
return;
}
if (!initialize) {
LOGGER.info("use local db service for init");
final String jdbcUrl = "jdbc:derby:" + Paths.get(EnvUtil.getNacosHome(), derbyBaseDir) + ";create=true";
initialize(jdbcUrl);
initialize = true;
}
}
@Override
public synchronized void reload() {
DataSource ds = jt.getDataSource();
if (ds == null) {
throw new RuntimeException("datasource is null");
}
try {
execute(ds.getConnection(), "META-INF/derby-schema.sql");
} catch (Exception e) {
if (LOGGER.isErrorEnabled()) {
LOGGER.error(e.getMessage(), e);
}
throw new NacosRuntimeException(NacosException.SERVER_ERROR, "load derby-schema.sql error.", e);
}
}
public DataSource getDatasource() {
return jt.getDataSource();
}
/**
* Clean and reopen Derby.
*
* @throws Exception exception.
*/
public void cleanAndReopenDerby() throws Exception {
doDerbyClean();
final String jdbcUrl =
"jdbc:derby:" + Paths.get(EnvUtil.getNacosHome(), derbyBaseDir).toString() + ";create=true";
initialize(jdbcUrl);
}
/**
* Restore derby.
*
* @param jdbcUrl jdbcUrl string value.
* @param callable callable.
* @throws Exception exception.
*/
public void restoreDerby(String jdbcUrl, Callable<Void> callable) throws Exception {
doDerbyClean();
callable.call();
initialize(jdbcUrl);
}
private void doDerbyClean() throws Exception {
LOGGER.warn("use local db service for reopenDerby");
try {
DriverManager.getConnection("jdbc:derby:;shutdown=true");
} catch (Exception e) {
// An error is thrown when the Derby shutdown is executed, which should be ignored
if (!StringUtils.containsIgnoreCase(e.getMessage(), derbyShutdownErrMsg)) {
throw e;
}
}
DiskUtils.deleteDirectory(Paths.get(EnvUtil.getNacosHome(), derbyBaseDir).toString());
}
private synchronized void initialize(String jdbcUrl) {
DataSourcePoolProperties poolProperties = DataSourcePoolProperties.build(EnvUtil.getEnvironment());
poolProperties.setDriverClassName(jdbcDriverName);
poolProperties.setJdbcUrl(jdbcUrl);
poolProperties.setUsername(userName);
poolProperties.setPassword(password);
HikariDataSource ds = poolProperties.getDataSource();
DataSourceTransactionManager tm = new DataSourceTransactionManager();
tm.setDataSource(ds);
if (jdbcTemplateInit) {
jt.setDataSource(ds);
tjt.setTransactionManager(tm);
} else {
jt = new JdbcTemplate();
jt.setMaxRows(50000);
jt.setQueryTimeout(5000);
jt.setDataSource(ds);
tjt = new TransactionTemplate(tm);
tjt.setTimeout(5000);
jdbcTemplateInit = true;
}
reload();
}
@Override
public boolean checkMasterWritable() {
return true;
}
@Override
public JdbcTemplate getJdbcTemplate() {
return jt;
}
@Override
public TransactionTemplate getTransactionTemplate() {
return tjt;
}
@Override
public String getCurrentDbUrl() {
return "jdbc:derby:" + EnvUtil.getNacosHome() + File.separator + derbyBaseDir + ";create=true";
}
@Override
public String getHealth() {
return healthStatus;
}
@Override
public String getDataSourceType() {
return dataSourceType;
}
public void setHealthStatus(String healthStatus) {
this.healthStatus = healthStatus;
}
/**
* Load sql.
*
* @param sqlFile sql.
* @return sqls.
* @throws Exception Exception.
*/
private List<String> loadSql(String sqlFile) throws Exception {
List<String> sqlList = new ArrayList<>();
InputStream sqlFileIn = null;
try {
File file = new File(
EnvUtil.getNacosHome() + File.separator + "conf" + File.separator + "derby-schema.sql");
if (StringUtils.isBlank(EnvUtil.getNacosHome()) || !file.exists()) {
ClassLoader classLoader = getClass().getClassLoader();
URL url = classLoader.getResource(sqlFile);
sqlFileIn = url.openStream();
} else {
sqlFileIn = new FileInputStream(file);
}
StringBuilder sqlSb = new StringBuilder();
byte[] buff = new byte[1024];
int byteRead = 0;
while ((byteRead = sqlFileIn.read(buff)) != -1) {
sqlSb.append(new String(buff, 0, byteRead, PersistenceConstant.DEFAULT_ENCODE));
}
String[] sqlArr = sqlSb.toString().split(";");
for (int i = 0; i < sqlArr.length; i++) {
String sql = sqlArr[i].replaceAll("--.*", "").trim();
if (StringUtils.isNotEmpty(sql)) {
sqlList.add(sql);
}
}
return sqlList;
} catch (Exception ex) {
throw new Exception(ex.getMessage());
} finally {
IoUtils.closeQuietly(sqlFileIn);
}
}
/**
* Execute sql.
*
* @param conn connect.
* @param sqlFile sql.
* @throws Exception Exception.
*/
private void execute(Connection conn, String sqlFile) throws Exception {
try (Statement stmt = conn.createStatement()) {
List<String> sqlList = loadSql(sqlFile);
for (String sql : sqlList) {
try {
stmt.execute(sql);
} catch (Exception e) {
LOGGER.warn(e.getMessage());
}
}
}
}
}

View File

@ -0,0 +1,52 @@
/*
* Copyright 1999-2023 Alibaba Group Holding Ltd.
*
* 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 com.alibaba.nacos.persistence.exception;
import org.springframework.dao.DataAccessException;
/**
* NJdbcException.
*
* @author <a href="mailto:liaochuntao@live.com">liaochuntao</a>
*/
@SuppressWarnings("all")
public class NJdbcException extends DataAccessException {
private String originExceptionName;
public NJdbcException(String msg) {
super(msg);
}
public NJdbcException(String msg, String originExceptionName) {
super(msg);
this.originExceptionName = originExceptionName;
}
public NJdbcException(String msg, Throwable cause, String originExceptionName) {
super(msg, cause);
this.originExceptionName = originExceptionName;
}
public NJdbcException(String msg, Throwable cause) {
super(msg, cause);
}
public NJdbcException(Throwable cause) {
super("", cause);
}
}

View File

@ -0,0 +1,84 @@
/*
* Copyright 1999-2023 Alibaba Group Holding Ltd.
*
* 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 com.alibaba.nacos.persistence.model;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
/**
* Page.
*
* @author boyan
* @date 2010-5-6
*/
public class Page<E> implements Serializable {
static final long serialVersionUID = 1234544030560484292L;
/**
* totalCount.
*/
private int totalCount;
/**
* pageNumber.
*/
private int pageNumber;
/**
* pagesAvailable.
*/
private int pagesAvailable;
/**
* pageItems.
*/
private List<E> pageItems = new ArrayList<>();
public void setPageNumber(int pageNumber) {
this.pageNumber = pageNumber;
}
public void setPagesAvailable(int pagesAvailable) {
this.pagesAvailable = pagesAvailable;
}
public void setPageItems(List<E> pageItems) {
this.pageItems = pageItems;
}
public int getTotalCount() {
return totalCount;
}
public void setTotalCount(int totalCount) {
this.totalCount = totalCount;
}
public int getPageNumber() {
return pageNumber;
}
public int getPagesAvailable() {
return pagesAvailable;
}
public List<E> getPageItems() {
return pageItems;
}
}

View File

@ -0,0 +1,39 @@
/*
* Copyright 1999-2023 Alibaba Group Holding Ltd.
*
* 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 com.alibaba.nacos.persistence.model.event;
import com.alibaba.nacos.common.notify.SlowEvent;
/**
* Data import event.
*
* @author <a href="mailto:liaochuntao@live.com">liaochuntao</a>
*/
public class DerbyImportEvent extends SlowEvent {
private static final long serialVersionUID = 3299565864352399053L;
private final boolean finished;
public DerbyImportEvent(boolean finished) {
this.finished = finished;
}
public boolean isFinished() {
return finished;
}
}

View File

@ -0,0 +1,32 @@
/*
* Copyright 1999-2023 Alibaba Group Holding Ltd.
*
* 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 com.alibaba.nacos.persistence.model.event;
import com.alibaba.nacos.common.notify.SlowEvent;
/**
* DerbyLoadEvent.
*
* @author <a href="mailto:liaochuntao@live.com">liaochuntao</a>
*/
public class DerbyLoadEvent extends SlowEvent {
public static final DerbyLoadEvent INSTANCE = new DerbyLoadEvent();
private static final long serialVersionUID = 875401667921565121L;
}

View File

@ -0,0 +1,42 @@
/*
* Copyright 1999-2023 Alibaba Group Holding Ltd.
*
* 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 com.alibaba.nacos.persistence.model.event;
import com.alibaba.nacos.common.notify.SlowEvent;
/**
* RaftDBErrorEvent.
*
* @author <a href="mailto:liaochunyhm@live.com">liaochuntao</a>
*/
public class RaftDbErrorEvent extends SlowEvent {
private static final long serialVersionUID = 101591819161802336L;
private Throwable ex;
public RaftDbErrorEvent() {
}
public RaftDbErrorEvent(Throwable ex) {
this.ex = ex;
}
public Throwable getEx() {
return ex;
}
}

View File

@ -0,0 +1,34 @@
/*
* Copyright 1999-2023 Alibaba Group Holding Ltd.
*
* 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 com.alibaba.nacos.persistence.monitor;
import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.Metrics;
/**
* Metrics for datasource.
*
* @author xiweng.yy
*/
public class DatasourceMetrics {
public static Counter getDbException() {
// TODO: After {@code NacosMeterRegistryCenter} move to more basic module, the usage can be changed.
// TODO: Current {@code NacosMeterRegistryCenter} is in core module, but core module maybe depend persistence to save namespace.
return Metrics.counter("nacos_exception", "module", "config", "name", "db");
}
}

View File

@ -0,0 +1,50 @@
/*
* Copyright 1999-2023 Alibaba Group Holding Ltd.
*
* 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 com.alibaba.nacos.persistence.repository;
import com.alibaba.nacos.persistence.model.Page;
import com.alibaba.nacos.plugin.datasource.model.MapperResult;
import org.springframework.jdbc.core.RowMapper;
/**
* Pagination Utils interface.
*
* @author <a href="mailto:liaochuntao@live.com">liaochuntao</a>
*/
@SuppressWarnings("PMD.AbstractMethodOrInterfaceMethodMustUseJavadocRule")
public interface PaginationHelper<E> {
Page<E> fetchPage(final String sqlCountRows, final String sqlFetchRows, final Object[] args, final int pageNo,
final int pageSize, final RowMapper<E> rowMapper);
Page<E> fetchPage(final String sqlCountRows, final String sqlFetchRows, final Object[] args, final int pageNo,
final int pageSize, final Long lastMaxId, final RowMapper<E> rowMapper);
Page<E> fetchPageLimit(final String sqlCountRows, final String sqlFetchRows, final Object[] args, final int pageNo,
final int pageSize, final RowMapper<E> rowMapper);
Page<E> fetchPageLimit(final String sqlCountRows, final Object[] args1, final String sqlFetchRows,
final Object[] args2, final int pageNo, final int pageSize, final RowMapper<E> rowMapper);
Page<E> fetchPageLimit(final String sqlFetchRows, final Object[] args, final int pageNo, final int pageSize,
final RowMapper<E> rowMapper);
Page<E> fetchPageLimit(final MapperResult countMapperResult, final MapperResult mapperResult, final int pageNo,
final int pageSize, final RowMapper<E> rowMapper);
void updateLimit(final String sql, final Object[] args);
}

View File

@ -0,0 +1,82 @@
/*
* Copyright 1999-2023 Alibaba Group Holding Ltd.
*
* 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 com.alibaba.nacos.persistence.repository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.core.RowMapper;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* Manager RowMapper {@link RowMapper} for database object mapping.
*
* @author <a href="mailto:liaochuntao@live.com">liaochuntao</a>
*/
public final class RowMapperManager {
private static final Logger LOGGER = LoggerFactory.getLogger(RowMapperManager.class);
public static final MapRowMapper MAP_ROW_MAPPER = new MapRowMapper();
public static Map<String, RowMapper> mapperMap = new HashMap<>(16);
static {
// MAP_ROW_MAPPER
mapperMap.put(MAP_ROW_MAPPER.getClass().getCanonicalName(), MAP_ROW_MAPPER);
}
public static <D> RowMapper<D> getRowMapper(String classFullName) {
return (RowMapper<D>) mapperMap.get(classFullName);
}
/**
* Register custom row mapper to manager.
*
* @param classFullName full class name of row mapper handled.
* @param rowMapper row mapper
* @param <D> class of row mapper handled
*/
public static synchronized <D> void registerRowMapper(String classFullName, RowMapper<D> rowMapper) {
if (mapperMap.containsKey(classFullName)) {
LOGGER.warn("row mapper {} conflicts, {} will be replaced by {}", classFullName,
mapperMap.get(classFullName).getClass().getCanonicalName(),
rowMapper.getClass().getCanonicalName());
}
mapperMap.put(classFullName, rowMapper);
}
public static final class MapRowMapper implements RowMapper<Map<String, Object>> {
@Override
public Map<String, Object> mapRow(ResultSet resultSet, int rowNum) throws SQLException {
ResultSetMetaData metaData = resultSet.getMetaData();
int columnCount = metaData.getColumnCount();
Map<String, Object> map = new LinkedHashMap<>(columnCount);
for (int i = 1; i <= columnCount; i++) {
map.put(metaData.getColumnLabel(i), resultSet.getObject(i));
}
return map;
}
}
}

View File

@ -0,0 +1,198 @@
/*
* Copyright 1999-2023 Alibaba Group Holding Ltd.
*
* 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 com.alibaba.nacos.persistence.repository.embedded;
import com.alibaba.nacos.persistence.model.Page;
import com.alibaba.nacos.persistence.repository.PaginationHelper;
import com.alibaba.nacos.persistence.repository.embedded.operate.DatabaseOperate;
import com.alibaba.nacos.plugin.datasource.model.MapperResult;
import org.springframework.jdbc.core.RowMapper;
import java.util.List;
/**
* Pagination Utils For Apache Derby.
*
* @param <E> Generic class
* @author boyan
* @date 2010-5-6
*/
public class EmbeddedPaginationHelperImpl<E> implements PaginationHelper {
private final DatabaseOperate databaseOperate;
public EmbeddedPaginationHelperImpl(DatabaseOperate databaseOperate) {
this.databaseOperate = databaseOperate;
}
/**
* Take paging.
*
* @param sqlCountRows Query total SQL
* @param sqlFetchRows Query data sql
* @param args query args
* @param pageNo page number
* @param pageSize page size
* @param rowMapper Entity mapping
* @return Paging data
*/
@Override
public Page<E> fetchPage(final String sqlCountRows, final String sqlFetchRows, final Object[] args,
final int pageNo, final int pageSize, final RowMapper rowMapper) {
return fetchPage(sqlCountRows, sqlFetchRows, args, pageNo, pageSize, null, rowMapper);
}
@Override
public Page<E> fetchPage(final String sqlCountRows, final String sqlFetchRows, Object[] args, final int pageNo,
final int pageSize, final Long lastMaxId, final RowMapper rowMapper) {
if (pageNo <= 0 || pageSize <= 0) {
throw new IllegalArgumentException("pageNo and pageSize must be greater than zero");
}
// Query the total number of current records
Integer rowCountInt = databaseOperate.queryOne(sqlCountRows, args, Integer.class);
if (rowCountInt == null) {
throw new IllegalArgumentException("fetchPageLimit error");
}
// Count pages
int pageCount = rowCountInt / pageSize;
if (rowCountInt > pageSize * pageCount) {
pageCount++;
}
// Create Page object
final Page<E> page = new Page<>();
page.setPageNumber(pageNo);
page.setPagesAvailable(pageCount);
page.setTotalCount(rowCountInt);
if (pageNo > pageCount) {
return page;
}
List<E> result = databaseOperate.queryMany(sqlFetchRows, args, rowMapper);
for (E item : result) {
page.getPageItems().add(item);
}
return page;
}
@Override
public Page<E> fetchPageLimit(final String sqlCountRows, final String sqlFetchRows, final Object[] args,
final int pageNo, final int pageSize, final RowMapper rowMapper) {
if (pageNo <= 0 || pageSize <= 0) {
throw new IllegalArgumentException("pageNo and pageSize must be greater than zero");
}
// Query the total number of current records
Integer rowCountInt = databaseOperate.queryOne(sqlCountRows, Integer.class);
if (rowCountInt == null) {
throw new IllegalArgumentException("fetchPageLimit error");
}
// Count pages
int pageCount = rowCountInt / pageSize;
if (rowCountInt > pageSize * pageCount) {
pageCount++;
}
// Create Page object
final Page<E> page = new Page<>();
page.setPageNumber(pageNo);
page.setPagesAvailable(pageCount);
page.setTotalCount(rowCountInt);
if (pageNo > pageCount) {
return page;
}
List<E> result = databaseOperate.queryMany(sqlFetchRows, args, rowMapper);
for (E item : result) {
page.getPageItems().add(item);
}
return page;
}
@Override
public Page<E> fetchPageLimit(final String sqlCountRows, final Object[] args1, final String sqlFetchRows,
final Object[] args2, final int pageNo, final int pageSize, final RowMapper rowMapper) {
if (pageNo <= 0 || pageSize <= 0) {
throw new IllegalArgumentException("pageNo and pageSize must be greater than zero");
}
// Query the total number of current records
Integer rowCountInt = databaseOperate.queryOne(sqlCountRows, args1, Integer.class);
if (rowCountInt == null) {
throw new IllegalArgumentException("fetchPageLimit error");
}
// Count pages
int pageCount = rowCountInt / pageSize;
if (rowCountInt > pageSize * pageCount) {
pageCount++;
}
// Create Page object
final Page<E> page = new Page<>();
page.setPageNumber(pageNo);
page.setPagesAvailable(pageCount);
page.setTotalCount(rowCountInt);
if (pageNo > pageCount) {
return page;
}
List<E> result = databaseOperate.queryMany(sqlFetchRows, args2, rowMapper);
for (E item : result) {
page.getPageItems().add(item);
}
return page;
}
@Override
public Page<E> fetchPageLimit(final String sqlFetchRows, final Object[] args, final int pageNo, final int pageSize,
final RowMapper rowMapper) {
if (pageNo <= 0 || pageSize <= 0) {
throw new IllegalArgumentException("pageNo and pageSize must be greater than zero");
}
// Create Page object
final Page<E> page = new Page<>();
List<E> result = databaseOperate.queryMany(sqlFetchRows, args, rowMapper);
for (E item : result) {
page.getPageItems().add(item);
}
return page;
}
@Override
public Page fetchPageLimit(MapperResult countMapperResult, MapperResult mapperResult, int pageNo, int pageSize,
RowMapper rowMapper) {
return fetchPageLimit(countMapperResult.getSql(), countMapperResult.getParamList().toArray(),
mapperResult.getSql(), mapperResult.getParamList().toArray(), pageNo, pageSize, rowMapper);
}
@Override
public void updateLimit(final String sql, final Object[] args) {
EmbeddedStorageContextHolder.addSqlContext(sql, args);
try {
databaseOperate.update(EmbeddedStorageContextHolder.getCurrentSqlContext());
} finally {
EmbeddedStorageContextHolder.cleanAllContext();
}
}
}

View File

@ -0,0 +1,119 @@
/*
* Copyright 1999-2023 Alibaba Group Holding Ltd.
*
* 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 com.alibaba.nacos.persistence.repository.embedded;
import com.alibaba.nacos.persistence.repository.embedded.sql.ModifyRequest;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Embedded storae context holder.
*
* @author xiweng.yy
*/
public class EmbeddedStorageContextHolder {
private static final ThreadLocal<ArrayList<ModifyRequest>> SQL_CONTEXT = ThreadLocal.withInitial(ArrayList::new);
private static final ThreadLocal<Map<String, String>> EXTEND_INFO_CONTEXT = ThreadLocal.withInitial(HashMap::new);
/**
* Add sql context.
*
* @param sql sql
* @param args argument list
*/
public static void addSqlContext(String sql, Object... args) {
ArrayList<ModifyRequest> requests = SQL_CONTEXT.get();
ModifyRequest context = new ModifyRequest();
context.setExecuteNo(requests.size());
context.setSql(sql);
context.setArgs(args);
requests.add(context);
SQL_CONTEXT.set(requests);
}
/**
* Add sql context.
*
* @param rollbackOnUpdateFail roll back when update fail
* @param sql sql
* @param args argument list
*/
public static void addSqlContext(boolean rollbackOnUpdateFail, String sql, Object... args) {
ArrayList<ModifyRequest> requests = SQL_CONTEXT.get();
ModifyRequest context = new ModifyRequest();
context.setExecuteNo(requests.size());
context.setSql(sql);
context.setArgs(args);
context.setRollBackOnUpdateFail(rollbackOnUpdateFail);
requests.add(context);
SQL_CONTEXT.set(requests);
}
/**
* Put extend info.
*
* @param key key
* @param value value
*/
public static void putExtendInfo(String key, String value) {
Map<String, String> old = EXTEND_INFO_CONTEXT.get();
old.put(key, value);
EXTEND_INFO_CONTEXT.set(old);
}
/**
* Put all extend info.
*
* @param map all extend info
*/
public static void putAllExtendInfo(Map<String, String> map) {
Map<String, String> old = EXTEND_INFO_CONTEXT.get();
old.putAll(map);
EXTEND_INFO_CONTEXT.set(old);
}
/**
* Determine if key is included.
*
* @param key key
* @return {@code true} if contains key
*/
public static boolean containsExtendInfo(String key) {
Map<String, String> extendInfo = EXTEND_INFO_CONTEXT.get();
boolean exist = extendInfo.containsKey(key);
EXTEND_INFO_CONTEXT.set(extendInfo);
return exist;
}
public static List<ModifyRequest> getCurrentSqlContext() {
return SQL_CONTEXT.get();
}
public static Map<String, String> getCurrentExtendInfo() {
return EXTEND_INFO_CONTEXT.get();
}
public static void cleanAllContext() {
SQL_CONTEXT.remove();
EXTEND_INFO_CONTEXT.remove();
}
}

View File

@ -0,0 +1,41 @@
/*
* Copyright 1999-2023 Alibaba Group Holding Ltd.
*
* 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 com.alibaba.nacos.persistence.repository.embedded.hook;
import com.alibaba.nacos.consistency.entity.WriteRequest;
/**
* Embedded storage apply hook.
*
* <p>Async Hook after embedded storage apply raft log.</p>
*
* @author xiweng.yy
*/
@SuppressWarnings("PMD.AbstractClassShouldStartWithAbstractNamingRule")
public abstract class EmbeddedApplyHook {
protected EmbeddedApplyHook() {
EmbeddedApplyHookHolder.getInstance().register(this);
}
/**
* Called after apply finished.
*
* @param log raft log
*/
public abstract void afterApply(WriteRequest log);
}

View File

@ -0,0 +1,48 @@
/*
* Copyright 1999-2023 Alibaba Group Holding Ltd.
*
* 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 com.alibaba.nacos.persistence.repository.embedded.hook;
import java.util.HashSet;
import java.util.Set;
/**
* Holder for Embedded apply hook.
*
* @author xiweng.yy
*/
public class EmbeddedApplyHookHolder {
private static final EmbeddedApplyHookHolder INSTANCE = new EmbeddedApplyHookHolder();
private final Set<EmbeddedApplyHook> hooks;
private EmbeddedApplyHookHolder() {
hooks = new HashSet<>();
}
public static EmbeddedApplyHookHolder getInstance() {
return INSTANCE;
}
public void register(EmbeddedApplyHook hook) {
this.hooks.add(hook);
}
public Set<EmbeddedApplyHook> getAllHooks() {
return this.hooks;
}
}

View File

@ -0,0 +1,275 @@
/*
* Copyright 1999-2023 Alibaba Group Holding Ltd.
*
* 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 com.alibaba.nacos.persistence.repository.embedded.operate;
import com.alibaba.nacos.common.utils.ExceptionUtil;
import com.alibaba.nacos.common.utils.LoggerUtils;
import com.alibaba.nacos.persistence.repository.embedded.sql.ModifyRequest;
import com.alibaba.nacos.persistence.utils.DerbyUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.jdbc.BadSqlGrammarException;
import org.springframework.jdbc.CannotGetJdbcConnectionException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.transaction.IllegalTransactionStateException;
import org.springframework.transaction.support.TransactionTemplate;
import java.util.List;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.stream.IntStream;
/**
* The Derby database basic operation.
*
* @author <a href="mailto:liaochuntao@live.com">liaochuntao</a>
*/
@SuppressWarnings("PMD.AbstractMethodOrInterfaceMethodMustUseJavadocRule")
public interface BaseDatabaseOperate extends DatabaseOperate {
Logger LOGGER = LoggerFactory.getLogger(BaseDatabaseOperate.class);
/**
* query one result by sql then convert result to target type.
*
* @param jdbcTemplate {@link JdbcTemplate}
* @param sql sql
* @param cls target type
* @param <R> target type
* @return R
*/
default <R> R queryOne(JdbcTemplate jdbcTemplate, String sql, Class<R> cls) {
try {
return jdbcTemplate.queryForObject(sql, cls);
} catch (IncorrectResultSizeDataAccessException e) {
return null;
} catch (CannotGetJdbcConnectionException e) {
LOGGER.error("[db-error] can't get connection : {}", ExceptionUtil.getAllExceptionMsg(e));
throw e;
} catch (DataAccessException e) {
LOGGER.error("[db-error] DataAccessException : {}", ExceptionUtil.getAllExceptionMsg(e));
throw e;
}
}
/**
* query one result by sql and args then convert result to target type.
*
* @param jdbcTemplate {@link JdbcTemplate}
* @param sql sql
* @param args args
* @param cls target type
* @param <R> target type
* @return R
*/
default <R> R queryOne(JdbcTemplate jdbcTemplate, String sql, Object[] args, Class<R> cls) {
try {
return jdbcTemplate.queryForObject(sql, args, cls);
} catch (IncorrectResultSizeDataAccessException e) {
return null;
} catch (CannotGetJdbcConnectionException e) {
LOGGER.error("[db-error] {}", e.toString());
throw e;
} catch (DataAccessException e) {
LOGGER.error("[db-error] DataAccessException sql : {}, args : {}, error : {}", sql, args,
ExceptionUtil.getAllExceptionMsg(e));
throw e;
}
}
/**
* query one result by sql and args then convert result to target type through {@link RowMapper}.
*
* @param jdbcTemplate {@link JdbcTemplate}
* @param sql sql
* @param args args
* @param mapper {@link RowMapper}
* @param <R> target type
* @return R
*/
default <R> R queryOne(JdbcTemplate jdbcTemplate, String sql, Object[] args, RowMapper<R> mapper) {
try {
return jdbcTemplate.queryForObject(sql, args, mapper);
} catch (IncorrectResultSizeDataAccessException e) {
return null;
} catch (CannotGetJdbcConnectionException e) {
LOGGER.error("[db-error] {}", e.toString());
throw e;
} catch (DataAccessException e) {
LOGGER.error("[db-error] DataAccessException sql : {}, args : {}, error : {}", sql, args,
ExceptionUtil.getAllExceptionMsg(e));
throw e;
}
}
/**
* query many result by sql and args then convert result to target type through {@link RowMapper}.
*
* @param jdbcTemplate {@link JdbcTemplate}
* @param sql sql
* @param args args
* @param mapper {@link RowMapper}
* @param <R> target type
* @return result list
*/
default <R> List<R> queryMany(JdbcTemplate jdbcTemplate, String sql, Object[] args, RowMapper<R> mapper) {
try {
return jdbcTemplate.query(sql, args, mapper);
} catch (CannotGetJdbcConnectionException e) {
LOGGER.error("[db-error] {}", e.toString());
throw e;
} catch (DataAccessException e) {
LOGGER.error("[db-error] DataAccessException sql : {}, args : {}, error : {}", sql, args,
ExceptionUtil.getAllExceptionMsg(e));
throw e;
}
}
/**
* query many result by sql and args then convert result to target type.
*
* @param jdbcTemplate {@link JdbcTemplate}
* @param sql sql
* @param args args
* @param rClass target type class
* @param <R> target type
* @return result list
*/
default <R> List<R> queryMany(JdbcTemplate jdbcTemplate, String sql, Object[] args, Class<R> rClass) {
try {
return jdbcTemplate.queryForList(sql, args, rClass);
} catch (IncorrectResultSizeDataAccessException e) {
return null;
} catch (CannotGetJdbcConnectionException e) {
LOGGER.error("[db-error] {}", e.toString());
throw e;
} catch (DataAccessException e) {
LOGGER.error("[db-error] DataAccessException sql : {}, args : {}, error : {}", sql, args,
ExceptionUtil.getAllExceptionMsg(e));
throw e;
}
}
/**
* query many result by sql and args then convert result to List&lt;Map&lt;String, Object&gt;&gt;.
*
* @param jdbcTemplate {@link JdbcTemplate}
* @param sql sql
* @param args args
* @return List&lt;Map&lt;String, Object&gt;&gt;
*/
default List<Map<String, Object>> queryMany(JdbcTemplate jdbcTemplate, String sql, Object[] args) {
try {
return jdbcTemplate.queryForList(sql, args);
} catch (CannotGetJdbcConnectionException e) {
LOGGER.error("[db-error] {}", e.toString());
throw e;
} catch (DataAccessException e) {
LOGGER.error("[db-error] DataAccessException sql : {}, args : {}, error : {}", sql, args,
ExceptionUtil.getAllExceptionMsg(e));
throw e;
}
}
/**
* execute update operation.
*
* @param transactionTemplate {@link TransactionTemplate}
* @param jdbcTemplate {@link JdbcTemplate}
* @param contexts {@link List} ModifyRequest list
* @return {@link Boolean}
*/
default Boolean update(TransactionTemplate transactionTemplate, JdbcTemplate jdbcTemplate,
List<ModifyRequest> contexts) {
return update(transactionTemplate, jdbcTemplate, contexts, null);
}
/**
* execute update operation, to fix #3617.
*
* @param transactionTemplate {@link TransactionTemplate}
* @param jdbcTemplate {@link JdbcTemplate}
* @param contexts {@link List} ModifyRequest list
* @return {@link Boolean}
*/
default Boolean update(TransactionTemplate transactionTemplate, JdbcTemplate jdbcTemplate,
List<ModifyRequest> contexts, BiConsumer<Boolean, Throwable> consumer) {
boolean updateResult = Boolean.FALSE;
try {
updateResult = transactionTemplate.execute(status -> {
String[] errSql = new String[] {null};
Object[][] args = new Object[][] {null};
try {
contexts.forEach(pair -> {
errSql[0] = pair.getSql();
args[0] = pair.getArgs();
boolean rollBackOnUpdateFail = pair.isRollBackOnUpdateFail();
LoggerUtils.printIfDebugEnabled(LOGGER, "current sql : {}", errSql[0]);
LoggerUtils.printIfDebugEnabled(LOGGER, "current args : {}", args[0]);
int row = jdbcTemplate.update(pair.getSql(), pair.getArgs());
if (rollBackOnUpdateFail && row < 1) {
LoggerUtils.printIfDebugEnabled(LOGGER, "SQL update affected {} rows ", row);
throw new IllegalTransactionStateException("Illegal transaction");
}
});
if (consumer != null) {
consumer.accept(Boolean.TRUE, null);
}
return Boolean.TRUE;
} catch (BadSqlGrammarException | DataIntegrityViolationException e) {
LOGGER.error("[db-error] sql : {}, args : {}, error : {}", errSql[0], args[0], e.toString());
if (consumer != null) {
consumer.accept(Boolean.FALSE, e);
}
return Boolean.FALSE;
} catch (CannotGetJdbcConnectionException e) {
LOGGER.error("[db-error] sql : {}, args : {}, error : {}", errSql[0], args[0], e.toString());
throw e;
} catch (DataAccessException e) {
LOGGER.error("[db-error] DataAccessException sql : {}, args : {}, error : {}", errSql[0], args[0],
ExceptionUtil.getAllExceptionMsg(e));
throw e;
}
});
} catch (IllegalTransactionStateException e) {
LoggerUtils.printIfDebugEnabled(LOGGER, "Roll back transaction for {} ", e.getMessage());
if (consumer != null) {
consumer.accept(Boolean.FALSE, e);
}
}
return updateResult;
}
/**
* Perform data import.
*
* @param template {@link JdbcTemplate}
* @param requests {@link List} ModifyRequest list
* @return {@link Boolean}
*/
default Boolean doDataImport(JdbcTemplate template, List<ModifyRequest> requests) {
final String[] sql = requests.stream().map(ModifyRequest::getSql).map(DerbyUtils::insertStatementCorrection)
.toArray(String[]::new);
int[] affect = template.batchUpdate(sql);
return IntStream.of(affect).count() == requests.size();
}
}

View File

@ -0,0 +1,177 @@
/*
* Copyright 1999-2023 Alibaba Group Holding Ltd.
*
* 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 com.alibaba.nacos.persistence.repository.embedded.operate;
import com.alibaba.nacos.common.model.RestResult;
import com.alibaba.nacos.persistence.repository.embedded.EmbeddedStorageContextHolder;
import com.alibaba.nacos.persistence.repository.embedded.sql.ModifyRequest;
import org.springframework.jdbc.core.RowMapper;
import java.io.File;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.function.BiConsumer;
/**
* Derby database operation.
*
* @author <a href="mailto:liaochuntao@live.com">liaochuntao</a>
*/
public interface DatabaseOperate {
/**
* Data query transaction.
*
* @param sql sqk text
* @param cls target type
* @param <R> return type
* @return query result
*/
<R> R queryOne(String sql, Class<R> cls);
/**
* Data query transaction.
*
* @param sql sqk text
* @param args sql parameters
* @param cls target type
* @param <R> return type
* @return query result
*/
<R> R queryOne(String sql, Object[] args, Class<R> cls);
/**
* Data query transaction.
*
* @param sql sqk text
* @param args sql parameters
* @param mapper Database query result converter
* @param <R> return type
* @return query result
*/
<R> R queryOne(String sql, Object[] args, RowMapper<R> mapper);
/**
* Data query transaction.
*
* @param sql sqk text
* @param args sql parameters
* @param mapper Database query result converter
* @param <R> return type
* @return query result
*/
<R> List<R> queryMany(String sql, Object[] args, RowMapper<R> mapper);
/**
* Data query transaction.
*
* @param sql sqk text
* @param args sql parameters
* @param rClass target type
* @param <R> return type
* @return query result
*/
<R> List<R> queryMany(String sql, Object[] args, Class<R> rClass);
/**
* Data query transaction.
*
* @param sql sqk text
* @param args sql parameters
* @return query result
*/
List<Map<String, Object>> queryMany(String sql, Object[] args);
/**
* data modify transaction.
*
* @param modifyRequests {@link List}
* @param consumer {@link BiConsumer}
* @return is success
*/
Boolean update(List<ModifyRequest> modifyRequests, BiConsumer<Boolean, Throwable> consumer);
/**
* data modify transaction.
*
* @param modifyRequests {@link List}
* @return is success
*/
default Boolean update(List<ModifyRequest> modifyRequests) {
return update(modifyRequests, null);
}
/**
* data importing, This method is suitable for importing data from external data sources into embedded data
* sources.
*
* @param file {@link File}
* @return {@link CompletableFuture}
*/
CompletableFuture<RestResult<String>> dataImport(File file);
/**
* data modify transaction The SqlContext to be executed in the current thread will be executed and automatically
* cleared.
*
* @return is success
*/
default Boolean blockUpdate() {
return blockUpdate(null);
}
/**
* data modify transaction The SqlContext to be executed in the current thread will be executed and automatically
* cleared.
* @author klw(213539@qq.com)
* 2020/8/24 18:16
* @param consumer the consumer
* @return java.lang.Boolean
*/
default Boolean blockUpdate(BiConsumer<Boolean, Throwable> consumer) {
try {
return update(EmbeddedStorageContextHolder.getCurrentSqlContext(), consumer);
} finally {
EmbeddedStorageContextHolder.cleanAllContext();
}
}
/**
* data modify transaction The SqlContext to be executed in the current thread will be executed and automatically
* cleared.
*
* @return is success
*/
default CompletableFuture<Boolean> futureUpdate() {
try {
CompletableFuture<Boolean> future = new CompletableFuture<>();
update(EmbeddedStorageContextHolder.getCurrentSqlContext(), (o, throwable) -> {
if (Objects.nonNull(throwable)) {
future.completeExceptionally(throwable);
return;
}
future.complete(o);
});
return future;
} finally {
EmbeddedStorageContextHolder.cleanAllContext();
}
}
}

View File

@ -0,0 +1,144 @@
/*
* Copyright 1999-2023 Alibaba Group Holding Ltd.
*
* 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 com.alibaba.nacos.persistence.repository.embedded.operate;
import com.alibaba.nacos.common.model.RestResult;
import com.alibaba.nacos.common.model.RestResultUtils;
import com.alibaba.nacos.common.utils.CollectionUtils;
import com.alibaba.nacos.common.utils.StringUtils;
import com.alibaba.nacos.persistence.configuration.condition.ConditionStandaloneEmbedStorage;
import com.alibaba.nacos.persistence.datasource.DataSourceService;
import com.alibaba.nacos.persistence.datasource.DynamicDataSource;
import com.alibaba.nacos.persistence.repository.embedded.sql.ModifyRequest;
import com.alibaba.nacos.sys.utils.DiskUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Conditional;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Component;
import org.springframework.transaction.support.TransactionTemplate;
import javax.annotation.PostConstruct;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.BiConsumer;
import java.util.stream.Collectors;
/**
* Derby operation in stand-alone mode.
*
* @author <a href="mailto:liaochuntao@live.com">liaochuntao</a>
*/
@Conditional(ConditionStandaloneEmbedStorage.class)
@Component
public class StandaloneDatabaseOperateImpl implements BaseDatabaseOperate {
private static final Logger LOGGER = LoggerFactory.getLogger(StandaloneDatabaseOperateImpl.class);
private JdbcTemplate jdbcTemplate;
private TransactionTemplate transactionTemplate;
@PostConstruct
protected void init() {
DataSourceService dataSourceService = DynamicDataSource.getInstance().getDataSource();
jdbcTemplate = dataSourceService.getJdbcTemplate();
transactionTemplate = dataSourceService.getTransactionTemplate();
LOGGER.info("use StandaloneDatabaseOperateImpl");
}
@Override
public <R> R queryOne(String sql, Class<R> cls) {
return queryOne(jdbcTemplate, sql, cls);
}
@Override
public <R> R queryOne(String sql, Object[] args, Class<R> cls) {
return queryOne(jdbcTemplate, sql, args, cls);
}
@Override
public <R> R queryOne(String sql, Object[] args, RowMapper<R> mapper) {
return queryOne(jdbcTemplate, sql, args, mapper);
}
@Override
public <R> List<R> queryMany(String sql, Object[] args, RowMapper<R> mapper) {
return queryMany(jdbcTemplate, sql, args, mapper);
}
@Override
public <R> List<R> queryMany(String sql, Object[] args, Class<R> rClass) {
return queryMany(jdbcTemplate, sql, args, rClass);
}
@Override
public List<Map<String, Object>> queryMany(String sql, Object[] args) {
return queryMany(jdbcTemplate, sql, args);
}
@Override
public CompletableFuture<RestResult<String>> dataImport(File file) {
return CompletableFuture.supplyAsync(() -> {
try (DiskUtils.LineIterator iterator = DiskUtils.lineIterator(file)) {
int batchSize = 1000;
List<String> batchUpdate = new ArrayList<>(batchSize);
List<CompletableFuture<Void>> futures = new ArrayList<>();
List<Boolean> results = new CopyOnWriteArrayList<>();
while (iterator.hasNext()) {
String sql = iterator.next();
if (StringUtils.isNotBlank(sql)) {
batchUpdate.add(sql);
}
if (batchUpdate.size() == batchSize || !iterator.hasNext()) {
List<ModifyRequest> sqls = batchUpdate.stream().map(s -> {
ModifyRequest request = new ModifyRequest();
request.setSql(s);
return request;
}).collect(Collectors.toList());
futures.add(CompletableFuture.runAsync(() -> results.add(doDataImport(jdbcTemplate, sqls))));
batchUpdate.clear();
}
}
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
int code = 500;
if (!CollectionUtils.isEmpty(results)) {
code = (!results.stream().anyMatch(Boolean.FALSE::equals)) ? 200 : 500;
}
return RestResult.<String>builder().withCode(code).withData("").build();
} catch (Throwable ex) {
LOGGER.error("An exception occurred when external data was imported into Derby : ", ex);
return RestResultUtils.failed(ex.getMessage());
}
});
}
@Override
public Boolean update(List<ModifyRequest> modifyRequests, BiConsumer<Boolean, Throwable> consumer) {
return update(transactionTemplate, jdbcTemplate, modifyRequests, consumer);
}
@Override
public Boolean update(List<ModifyRequest> requestList) {
return update(transactionTemplate, jdbcTemplate, requestList);
}
}

View File

@ -0,0 +1,83 @@
/*
* Copyright 1999-2023 Alibaba Group Holding Ltd.
*
* 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 com.alibaba.nacos.persistence.repository.embedded.sql;
import java.io.Serializable;
import java.util.Arrays;
/**
* Represents a database UPDATE or INSERT or DELETE statement.
*
* @author <a href="mailto:liaochuntao@live.com">liaochuntao</a>
*/
@SuppressWarnings("PMD.ClassNamingShouldBeCamelRule")
public class ModifyRequest implements Serializable {
private static final long serialVersionUID = 4548851816596520564L;
private int executeNo;
private String sql;
private boolean rollBackOnUpdateFail = Boolean.FALSE;
private Object[] args;
public ModifyRequest() {
}
public ModifyRequest(String sql) {
this.sql = sql;
}
public int getExecuteNo() {
return executeNo;
}
public void setExecuteNo(int executeNo) {
this.executeNo = executeNo;
}
public String getSql() {
return sql;
}
public void setSql(String sql) {
this.sql = sql;
}
public Object[] getArgs() {
return args;
}
public void setArgs(Object[] args) {
this.args = args;
}
public boolean isRollBackOnUpdateFail() {
return rollBackOnUpdateFail;
}
public void setRollBackOnUpdateFail(boolean rollBackOnUpdateFail) {
this.rollBackOnUpdateFail = rollBackOnUpdateFail;
}
@Override
public String toString() {
return "SQL{" + "executeNo=" + executeNo + ", sql='" + sql + '\'' + ", args=" + Arrays.toString(args) + '}';
}
}

View File

@ -0,0 +1,58 @@
/*
* Copyright 1999-2023 Alibaba Group Holding Ltd.
*
* 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 com.alibaba.nacos.persistence.repository.embedded.sql;
import org.springframework.jdbc.core.RowMapper;
/**
* Associated with the method correspondence of the {@link org.springframework.jdbc.core.JdbcTemplate}.
*
* @author <a href="mailto:liaochuntao@live.com">liaochuntao</a>
*/
public class QueryType {
/**
* {@link org.springframework.jdbc.core.JdbcTemplate#queryForObject(String, RowMapper)}.
*/
public static final byte QUERY_ONE_WITH_MAPPER_WITH_ARGS = 0;
/**
* {@link org.springframework.jdbc.core.JdbcTemplate#queryForObject(String, Class)}.
*/
public static final byte QUERY_ONE_NO_MAPPER_NO_ARGS = 1;
/**
* {@link org.springframework.jdbc.core.JdbcTemplate#queryForObject(String, Object[], Class)}.
*/
public static final byte QUERY_ONE_NO_MAPPER_WITH_ARGS = 2;
/**
* {@link org.springframework.jdbc.core.JdbcTemplate#query(String, Object[], RowMapper)}.
*/
public static final byte QUERY_MANY_WITH_MAPPER_WITH_ARGS = 3;
/**
* {@link org.springframework.jdbc.core.JdbcTemplate#queryForList(String, Object...)}.
*/
public static final byte QUERY_MANY_WITH_LIST_WITH_ARGS = 4;
/**
* {@link org.springframework.jdbc.core.JdbcTemplate#queryForList(String, Object[], Class)}.
*/
public static final byte QUERY_MANY_NO_MAPPER_WITH_ARGS = 5;
}

View File

@ -0,0 +1,128 @@
/*
* Copyright 1999-2023 Alibaba Group Holding Ltd.
*
* 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 com.alibaba.nacos.persistence.repository.embedded.sql;
import java.io.Serializable;
import java.util.Arrays;
/**
* Represents a database SELECT statement.
*
* @author <a href="mailto:liaochuntao@live.com">liaochuntao</a>
*/
public class SelectRequest implements Serializable {
private static final long serialVersionUID = 2212052574976898602L;
private byte queryType;
private String sql;
private Object[] args;
private String className;
public byte getQueryType() {
return queryType;
}
public void setQueryType(byte queryType) {
this.queryType = queryType;
}
public String getSql() {
return sql;
}
public void setSql(String sql) {
this.sql = sql;
}
public Object[] getArgs() {
return args;
}
public void setArgs(Object[] args) {
this.args = args;
}
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
@Override
public String toString() {
return "SelectRequest{" + "queryType=" + queryType + ", sql='" + sql + '\'' + ", args=" + Arrays.toString(args)
+ ", className='" + className + '\'' + '}';
}
public static SelectRequestBuilder builder() {
return new SelectRequestBuilder();
}
public static final class SelectRequestBuilder {
private byte queryType;
private String sql;
private Object[] args;
private String className = null;
private SelectRequestBuilder() {
}
public SelectRequestBuilder queryType(byte queryType) {
this.queryType = queryType;
return this;
}
public SelectRequestBuilder sql(String sql) {
this.sql = sql;
return this;
}
public SelectRequestBuilder args(Object[] args) {
this.args = args;
return this;
}
public SelectRequestBuilder className(String className) {
this.className = className;
return this;
}
/**
* build select request.
*
* @return {@link SelectRequest}
*/
public SelectRequest build() {
SelectRequest request = new SelectRequest();
request.setQueryType(queryType);
request.setSql(sql);
request.setArgs(args);
request.setClassName(className);
return request;
}
}
}

View File

@ -0,0 +1,212 @@
/*
* Copyright 1999-2023 Alibaba Group Holding Ltd.
*
* 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 com.alibaba.nacos.persistence.repository.extrnal;
import com.alibaba.nacos.persistence.model.Page;
import com.alibaba.nacos.persistence.repository.PaginationHelper;
import com.alibaba.nacos.persistence.repository.embedded.EmbeddedStorageContextHolder;
import com.alibaba.nacos.plugin.datasource.model.MapperResult;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import java.util.List;
/**
* External Storage Pagination utils.
*
* @author <a href="mailto:liaochuntao@live.com">liaochuntao</a>
*/
public class ExternalStoragePaginationHelperImpl<E> implements PaginationHelper {
private final JdbcTemplate jdbcTemplate;
public ExternalStoragePaginationHelperImpl(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
/**
* Take paging.
*
* @param sqlCountRows query total SQL
* @param sqlFetchRows query data sql
* @param args query parameters
* @param pageNo page number
* @param pageSize page size
* @param rowMapper {@link RowMapper}
* @return Paginated data {@code <E>}
*/
@Override
public Page<E> fetchPage(final String sqlCountRows, final String sqlFetchRows, final Object[] args,
final int pageNo, final int pageSize, final RowMapper rowMapper) {
return fetchPage(sqlCountRows, sqlFetchRows, args, pageNo, pageSize, null, rowMapper);
}
@Override
public Page<E> fetchPage(final String sqlCountRows, final String sqlFetchRows, Object[] args, final int pageNo,
final int pageSize, final Long lastMaxId, final RowMapper rowMapper) {
if (pageNo <= 0 || pageSize <= 0) {
throw new IllegalArgumentException("pageNo and pageSize must be greater than zero");
}
// Query the total number of current records.
Integer rowCountInt = jdbcTemplate.queryForObject(sqlCountRows, args, Integer.class);
if (rowCountInt == null) {
throw new IllegalArgumentException("fetchPageLimit error");
}
// Compute pages count
int pageCount = rowCountInt / pageSize;
if (rowCountInt > pageSize * pageCount) {
pageCount++;
}
// Create Page object
final Page<E> page = new Page<>();
page.setPageNumber(pageNo);
page.setPagesAvailable(pageCount);
page.setTotalCount(rowCountInt);
if (pageNo > pageCount) {
return page;
}
List<E> result = jdbcTemplate.query(sqlFetchRows, args, rowMapper);
for (E item : result) {
page.getPageItems().add(item);
}
return page;
}
@Override
public Page<E> fetchPageLimit(final String sqlCountRows, final String sqlFetchRows, final Object[] args,
final int pageNo, final int pageSize, final RowMapper rowMapper) {
if (pageNo <= 0 || pageSize <= 0) {
throw new IllegalArgumentException("pageNo and pageSize must be greater than zero");
}
// Query the total number of current records
Integer rowCountInt = jdbcTemplate.queryForObject(sqlCountRows, Integer.class);
if (rowCountInt == null) {
throw new IllegalArgumentException("fetchPageLimit error");
}
// Compute pages count
int pageCount = rowCountInt / pageSize;
if (rowCountInt > pageSize * pageCount) {
pageCount++;
}
// Create Page object
final Page<E> page = new Page<>();
page.setPageNumber(pageNo);
page.setPagesAvailable(pageCount);
page.setTotalCount(rowCountInt);
if (pageNo > pageCount) {
return page;
}
List<E> result = jdbcTemplate.query(sqlFetchRows, args, rowMapper);
for (E item : result) {
page.getPageItems().add(item);
}
return page;
}
@Override
public Page fetchPageLimit(MapperResult countMapperResult, MapperResult mapperResult, int pageNo, int pageSize,
RowMapper rowMapper) {
return fetchPageLimit(countMapperResult.getSql(), countMapperResult.getParamList().toArray(),
mapperResult.getSql(), mapperResult.getParamList().toArray(), pageNo, pageSize, rowMapper);
}
@Override
public Page<E> fetchPageLimit(final String sqlCountRows, final Object[] args1, final String sqlFetchRows,
final Object[] args2, final int pageNo, final int pageSize, final RowMapper rowMapper) {
if (pageNo <= 0 || pageSize <= 0) {
throw new IllegalArgumentException("pageNo and pageSize must be greater than zero");
}
// Query the total number of current records
Integer rowCountInt = jdbcTemplate.queryForObject(sqlCountRows, args1, Integer.class);
if (rowCountInt == null) {
throw new IllegalArgumentException("fetchPageLimit error");
}
// Compute pages count
int pageCount = rowCountInt / pageSize;
if (rowCountInt > pageSize * pageCount) {
pageCount++;
}
// Create Page object
final Page<E> page = new Page<>();
page.setPageNumber(pageNo);
page.setPagesAvailable(pageCount);
page.setTotalCount(rowCountInt);
if (pageNo > pageCount) {
return page;
}
List<E> result = jdbcTemplate.query(sqlFetchRows, args2, rowMapper);
for (E item : result) {
page.getPageItems().add(item);
}
return page;
}
@Override
public Page<E> fetchPageLimit(final String sqlFetchRows, final Object[] args, final int pageNo, final int pageSize,
final RowMapper rowMapper) {
if (pageNo <= 0 || pageSize <= 0) {
throw new IllegalArgumentException("pageNo and pageSize must be greater than zero");
}
// Create Page object
final Page<E> page = new Page<>();
List<E> result = jdbcTemplate.query(sqlFetchRows, args, rowMapper);
for (E item : result) {
page.getPageItems().add(item);
}
return page;
}
@Override
public void updateLimit(final String sql, final Object[] args) {
try {
jdbcTemplate.update(sql, args);
} finally {
EmbeddedStorageContextHolder.cleanAllContext();
}
}
/**
* Update limit with response.
*
* @param sql sql
* @param args args
* @return update row count
*/
public int updateLimitWithResponse(final String sql, final Object[] args) {
String sqlUpdate = sql;
try {
return jdbcTemplate.update(sqlUpdate, args);
} finally {
EmbeddedStorageContextHolder.cleanAllContext();
}
}
}

View File

@ -0,0 +1,54 @@
/*
* Copyright 1999-2023 Alibaba Group Holding Ltd.
*
* 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 com.alibaba.nacos.persistence.utils;
import com.zaxxer.hikari.HikariDataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* DataSource Connection CheckUtil.
*
* @author Long Yu
*/
public class ConnectionCheckUtil {
private static final Logger LOGGER = LoggerFactory.getLogger(ConnectionCheckUtil.class);
/**
* check HikariDataSource connection ,avoid [no datasource set] text.
*
* @param ds HikariDataSource object
*/
public static void checkDataSourceConnection(HikariDataSource ds) {
java.sql.Connection connection = null;
try {
connection = ds.getConnection();
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
if (connection != null) {
try {
connection.close();
} catch (Exception e) {
LOGGER.error(e.getMessage(), e);
}
}
}
}
}

View File

@ -0,0 +1,43 @@
/*
* Copyright 1999-2023 Alibaba Group Holding Ltd.
*
* 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 com.alibaba.nacos.persistence.utils;
import com.alibaba.nacos.common.utils.StringUtils;
import com.alibaba.nacos.persistence.constants.PersistenceConstant;
import com.alibaba.nacos.sys.env.EnvUtil;
/**
* get datasource platform util.
*
* @author lixiaoshuang
*/
public class DatasourcePlatformUtil {
/**
* get datasource platform.
*
* @param defaultPlatform default platform.
* @return
*/
public static String getDatasourcePlatform(String defaultPlatform) {
String platform = EnvUtil.getProperty(PersistenceConstant.DATASOURCE_PLATFORM_PROPERTY, defaultPlatform);
if (StringUtils.isBlank(platform)) {
platform = EnvUtil.getProperty(PersistenceConstant.DATASOURCE_PLATFORM_PROPERTY_OLD, defaultPlatform);
}
return platform;
}
}

View File

@ -0,0 +1,50 @@
/*
* Copyright 1999-2023 Alibaba Group Holding Ltd.
*
* 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 com.alibaba.nacos.persistence.utils;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Derby util.
*
* @author <a href="mailto:liaochuntao@live.com">liaochuntao</a>
*/
public final class DerbyUtils {
private static final String INSERT_INTO_VALUES = "(INSERT INTO .+? VALUES)";
private static final Pattern INSERT_INTO_PATTERN = Pattern.compile(INSERT_INTO_VALUES);
/**
* Because Derby's database table name is uppercase, you need to do a conversion to the insert statement that was
* inserted.
*
* @param sql external database insert sql
* @return derby insert sql
*/
public static String insertStatementCorrection(String sql) {
Matcher matcher = INSERT_INTO_PATTERN.matcher(sql);
if (!matcher.find()) {
return sql;
}
final String target = matcher.group(0);
final String upperCase = target.toUpperCase().replace("`", "");
return sql.replaceFirst(INSERT_INTO_VALUES, upperCase).replace(";", "");
}
}

View File

@ -0,0 +1,57 @@
/*
* Copyright 1999-2023 Alibaba Group Holding Ltd.
*
* 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 com.alibaba.nacos.persistence.utils;
import com.alibaba.nacos.common.executor.ExecutorFactory;
import com.alibaba.nacos.common.executor.NameThreadFactory;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
* Persistence async task executors.
*
* @author xiweng.yy
*/
public class PersistenceExecutor {
private static final ScheduledExecutorService TIMER_EXECUTOR = ExecutorFactory.Managed
.newScheduledExecutorService(PersistenceExecutor.class.getCanonicalName(), 2,
new NameThreadFactory("com.alibaba.nacos.persistence.timer"));
private static final Executor DUMP_EXECUTOR = ExecutorFactory.Managed
.newSingleExecutorService(PersistenceExecutor.class.getCanonicalName(),
new NameThreadFactory("com.alibaba.nacos.persistence.embedded.dump"));
private static final ExecutorService EMBEDDED_SNAPSHOT_EXECUTOR = ExecutorFactory.Managed
.newSingleExecutorService(PersistenceExecutor.class.getCanonicalName(),
new NameThreadFactory("com.alibaba.nacos.persistence.embedded.snapshot"));
public static void scheduleTask(Runnable command, long initialDelay, long delay, TimeUnit unit) {
TIMER_EXECUTOR.scheduleWithFixedDelay(command, initialDelay, delay, unit);
}
public static void executeEmbeddedDump(Runnable runnable) {
DUMP_EXECUTOR.execute(runnable);
}
public static void executeSnapshot(Runnable runnable) {
EMBEDDED_SNAPSHOT_EXECUTOR.execute(runnable);
}
}

View File

@ -0,0 +1,2 @@
org.springframework.context.ApplicationContextInitializer=\
com.alibaba.nacos.persistence.configuration.DatasourceConfiguration

View File

@ -0,0 +1,74 @@
/*
* Copyright 1999-2023 Alibaba Group Holding Ltd.
*
* 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 com.alibaba.nacos.persistence.configuration.condition;
import com.alibaba.nacos.persistence.configuration.DatasourceConfiguration;
import com.alibaba.nacos.sys.env.EnvUtil;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
public class ConditionDistributedEmbedStorageTest {
private ConditionDistributedEmbedStorage conditionDistributedEmbedStorage;
@Mock
ConditionContext context;
@Mock
AnnotatedTypeMetadata metadata;
@Before
public void init() {
conditionDistributedEmbedStorage = new ConditionDistributedEmbedStorage();
}
@Test
public void testMatches() {
MockedStatic<DatasourceConfiguration> propertyUtilMockedStatic = Mockito
.mockStatic(DatasourceConfiguration.class);
MockedStatic<EnvUtil> envUtilMockedStatic = Mockito.mockStatic(EnvUtil.class);
propertyUtilMockedStatic.when(DatasourceConfiguration::isEmbeddedStorage).thenReturn(true);
envUtilMockedStatic.when(EnvUtil::getStandaloneMode).thenReturn(true);
Assert.assertFalse(conditionDistributedEmbedStorage.matches(context, metadata));
Mockito.when(DatasourceConfiguration.isEmbeddedStorage()).thenReturn(true);
Mockito.when(EnvUtil.getStandaloneMode()).thenReturn(false);
propertyUtilMockedStatic.when(DatasourceConfiguration::isEmbeddedStorage).thenReturn(true);
envUtilMockedStatic.when(EnvUtil::getStandaloneMode).thenReturn(false);
Assert.assertTrue(conditionDistributedEmbedStorage.matches(context, metadata));
propertyUtilMockedStatic.when(DatasourceConfiguration::isEmbeddedStorage).thenReturn(false);
envUtilMockedStatic.when(EnvUtil::getStandaloneMode).thenReturn(true);
Assert.assertFalse(conditionDistributedEmbedStorage.matches(context, metadata));
propertyUtilMockedStatic.when(DatasourceConfiguration::isEmbeddedStorage).thenReturn(false);
envUtilMockedStatic.when(EnvUtil::getStandaloneMode).thenReturn(false);
Assert.assertFalse(conditionDistributedEmbedStorage.matches(context, metadata));
propertyUtilMockedStatic.close();
envUtilMockedStatic.close();
}
}

View File

@ -0,0 +1,56 @@
/*
* Copyright 1999-2023 Alibaba Group Holding Ltd.
*
* 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 com.alibaba.nacos.persistence.configuration.condition;
import com.alibaba.nacos.persistence.configuration.DatasourceConfiguration;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
public class ConditionOnEmbeddedStorageTest {
private ConditionOnEmbeddedStorage conditionOnEmbeddedStorage;
@Mock
ConditionContext context;
@Mock
AnnotatedTypeMetadata metadata;
@Before
public void init() {
conditionOnEmbeddedStorage = new ConditionOnEmbeddedStorage();
}
@Test
public void testMatches() {
MockedStatic<DatasourceConfiguration> mockedStatic = Mockito.mockStatic(DatasourceConfiguration.class);
mockedStatic.when(DatasourceConfiguration::isEmbeddedStorage).thenReturn(true);
Assert.assertTrue(conditionOnEmbeddedStorage.matches(context, metadata));
mockedStatic.when(DatasourceConfiguration::isEmbeddedStorage).thenReturn(false);
Assert.assertFalse(conditionOnEmbeddedStorage.matches(context, metadata));
mockedStatic.close();
}
}

View File

@ -0,0 +1,57 @@
/*
* Copyright 1999-2023 Alibaba Group Holding Ltd.
*
* 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 com.alibaba.nacos.persistence.configuration.condition;
import com.alibaba.nacos.persistence.configuration.DatasourceConfiguration;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
public class ConditionOnExternalStorageTest {
private ConditionOnExternalStorage conditionOnExternalStorage;
@Mock
ConditionContext context;
@Mock
AnnotatedTypeMetadata metadata;
@Before
public void init() {
conditionOnExternalStorage = new ConditionOnExternalStorage();
}
@Test
public void testMatches() {
MockedStatic<DatasourceConfiguration> mockedStatic = Mockito.mockStatic(DatasourceConfiguration.class);
mockedStatic.when(DatasourceConfiguration::isEmbeddedStorage).thenReturn(true);
Assert.assertFalse(conditionOnExternalStorage.matches(context, metadata));
mockedStatic.when(DatasourceConfiguration::isEmbeddedStorage).thenReturn(false);
Assert.assertTrue(conditionOnExternalStorage.matches(context, metadata));
mockedStatic.close();
}
}

View File

@ -0,0 +1,71 @@
/*
* Copyright 1999-2023 Alibaba Group Holding Ltd.
*
* 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 com.alibaba.nacos.persistence.configuration.condition;
import com.alibaba.nacos.persistence.configuration.DatasourceConfiguration;
import com.alibaba.nacos.sys.env.EnvUtil;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
public class ConditionStandaloneEmbedStorageTest {
private ConditionStandaloneEmbedStorage conditionStandaloneEmbedStorage;
@Mock
ConditionContext context;
@Mock
AnnotatedTypeMetadata metadata;
@Before
public void init() {
conditionStandaloneEmbedStorage = new ConditionStandaloneEmbedStorage();
}
@Test
public void testMatches() {
MockedStatic<DatasourceConfiguration> propertyUtilMockedStatic = Mockito
.mockStatic(DatasourceConfiguration.class);
MockedStatic<EnvUtil> envUtilMockedStatic = Mockito.mockStatic(EnvUtil.class);
propertyUtilMockedStatic.when(DatasourceConfiguration::isEmbeddedStorage).thenReturn(true);
envUtilMockedStatic.when(EnvUtil::getStandaloneMode).thenReturn(true);
Assert.assertTrue(conditionStandaloneEmbedStorage.matches(context, metadata));
propertyUtilMockedStatic.when(DatasourceConfiguration::isEmbeddedStorage).thenReturn(true);
envUtilMockedStatic.when(EnvUtil::getStandaloneMode).thenReturn(false);
Assert.assertFalse(conditionStandaloneEmbedStorage.matches(context, metadata));
propertyUtilMockedStatic.when(DatasourceConfiguration::isEmbeddedStorage).thenReturn(false);
envUtilMockedStatic.when(EnvUtil::getStandaloneMode).thenReturn(true);
Assert.assertFalse(conditionStandaloneEmbedStorage.matches(context, metadata));
propertyUtilMockedStatic.when(DatasourceConfiguration::isEmbeddedStorage).thenReturn(false);
envUtilMockedStatic.when(EnvUtil::getStandaloneMode).thenReturn(false);
Assert.assertFalse(conditionStandaloneEmbedStorage.matches(context, metadata));
propertyUtilMockedStatic.close();
envUtilMockedStatic.close();
}
}

View File

@ -0,0 +1,133 @@
/*
* Copyright 1999-2023 Alibaba Group Holding Ltd.
*
* 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 com.alibaba.nacos.persistence.datasource;
import com.alibaba.nacos.persistence.configuration.DatasourceConfiguration;
import com.alibaba.nacos.persistence.constants.PersistenceConstant;
import com.alibaba.nacos.sys.env.Constants;
import com.alibaba.nacos.sys.env.EnvUtil;
import org.junit.Assert;
import org.junit.Before;
import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.MethodSorters;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.mock.env.MockEnvironment;
import org.springframework.test.util.ReflectionTestUtils;
/**
* ClusterExternalStorage unit test.
*
* @author Long Yu
* @since 2.2.0
*/
@RunWith(MockitoJUnitRunner.class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class ClusterExternalStorageTest {
@InjectMocks
private DynamicDataSource dataSource;
private MockEnvironment environment;
@Mock
private LocalDataSourceServiceImpl localDataSourceService;
@Mock
private ExternalDataSourceServiceImpl basicDataSourceService;
DatasourceConfiguration datasourceConfig;
@Before
public void setUp() throws Exception {
environment = new MockEnvironment();
EnvUtil.setEnvironment(environment);
datasourceConfig = new DatasourceConfiguration();
dataSource = DynamicDataSource.getInstance();
ReflectionTestUtils.setField(dataSource, "localDataSourceService", localDataSourceService);
ReflectionTestUtils.setField(dataSource, "basicDataSourceService", basicDataSourceService);
}
@Test
public void test005WithClusterAndNullDatabase() {
// 模拟设置环境05指定集群未指定数据库UseExternalDB是true数据库类型是""
System.setProperty(Constants.STANDALONE_MODE_PROPERTY_NAME, "false");
environment.setProperty(PersistenceConstant.DATASOURCE_PLATFORM_PROPERTY_OLD, "");
EnvUtil.setIsStandalone(Boolean.getBoolean(Constants.STANDALONE_MODE_PROPERTY_NAME));
DatasourceConfiguration.setEmbeddedStorage(EnvUtil.getStandaloneMode());
// 模拟初始化
datasourceConfig.initialize(null);
Assert.assertFalse(EnvUtil.getStandaloneMode());
Assert.assertTrue(DatasourceConfiguration.isUseExternalDB());
Assert.assertTrue(dataSource.getDataSource() instanceof ExternalDataSourceServiceImpl);
}
@Test
public void test006WithClusterAndMysqlDatabase() {
// 模拟设置环境06指定集群指定数据库mysqlUseExternalDB是true数据库类型是mysql
System.setProperty(Constants.STANDALONE_MODE_PROPERTY_NAME, "false");
environment.setProperty(PersistenceConstant.DATASOURCE_PLATFORM_PROPERTY_OLD, "mysql");
EnvUtil.setIsStandalone(Boolean.getBoolean(Constants.STANDALONE_MODE_PROPERTY_NAME));
DatasourceConfiguration.setEmbeddedStorage(EnvUtil.getStandaloneMode());
// 模拟初始化
datasourceConfig.initialize(null);
Assert.assertFalse(EnvUtil.getStandaloneMode());
Assert.assertTrue(DatasourceConfiguration.isUseExternalDB());
Assert.assertTrue(dataSource.getDataSource() instanceof ExternalDataSourceServiceImpl);
}
@Test
public void test007WithClusterAndDerbyDatabase() {
// 模拟设置环境07指定集群指定数据库derbyUseExternalDB是false数据库类型是derby
System.setProperty(Constants.STANDALONE_MODE_PROPERTY_NAME, "false");
environment.setProperty(PersistenceConstant.DATASOURCE_PLATFORM_PROPERTY_OLD, "derby");
EnvUtil.setIsStandalone(Boolean.getBoolean(Constants.STANDALONE_MODE_PROPERTY_NAME));
DatasourceConfiguration.setEmbeddedStorage(true);
// 模拟初始化
datasourceConfig.initialize(null);
Assert.assertFalse(EnvUtil.getStandaloneMode());
Assert.assertFalse(DatasourceConfiguration.isUseExternalDB());
Assert.assertTrue(dataSource.getDataSource() instanceof LocalDataSourceServiceImpl);
}
@Test
public void test008WithClusterAndOtherDatabase() {
// 模拟设置环境08: 指定集群指定数据库其他UseExternalDB是true数据库类型是其他
System.setProperty(Constants.STANDALONE_MODE_PROPERTY_NAME, "false");
environment.setProperty(PersistenceConstant.DATASOURCE_PLATFORM_PROPERTY_OLD, "postgresql");
EnvUtil.setIsStandalone(Boolean.getBoolean(Constants.STANDALONE_MODE_PROPERTY_NAME));
DatasourceConfiguration.setEmbeddedStorage(EnvUtil.getStandaloneMode());
// 模拟初始化
datasourceConfig.initialize(null);
Assert.assertFalse(EnvUtil.getStandaloneMode());
Assert.assertTrue(DatasourceConfiguration.isUseExternalDB());
Assert.assertTrue(dataSource.getDataSource() instanceof ExternalDataSourceServiceImpl);
}
}

View File

@ -0,0 +1,69 @@
/*
* Copyright 1999-2023 Alibaba Group Holding Ltd.
*
* 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 com.alibaba.nacos.persistence.datasource;
import com.zaxxer.hikari.HikariDataSource;
import org.junit.Before;
import org.junit.Test;
import org.springframework.mock.env.MockEnvironment;
import static org.junit.Assert.assertEquals;
public class DataSourcePoolPropertiesTest {
private static final String JDBC_URL = "jdbc:derby://127.0.0.1:3306/nacos_devtest?characterEncoding=utf8&serverTimezone=UTC";
private static final String JDBC_DRIVER_CLASS_NAME = "org.apache.derby.jdbc.EmbeddedDriver";
private static final String PASSWORD = "nacos";
private static final String USERNAME = "nacos_devtest";
private static final Long CONNECTION_TIMEOUT = 10000L;
private static final Integer MAX_POOL_SIZE = 50;
private MockEnvironment environment;
@Before
public void setUp() throws Exception {
environment = new MockEnvironment();
environment.setProperty("db.user", USERNAME);
environment.setProperty("db.password", PASSWORD);
environment.setProperty("db.pool.config.connectionTimeout", CONNECTION_TIMEOUT.toString());
environment.setProperty("db.pool.config.maximumPoolSize", MAX_POOL_SIZE.toString());
}
@Test
public void testBuild() {
DataSourcePoolProperties poolProperties = DataSourcePoolProperties.build(environment);
poolProperties.setJdbcUrl(JDBC_URL);
poolProperties.setDriverClassName(JDBC_DRIVER_CLASS_NAME);
poolProperties.setUsername(USERNAME);
poolProperties.setPassword(PASSWORD);
HikariDataSource actual = poolProperties.getDataSource();
assertEquals(JDBC_URL, actual.getJdbcUrl());
assertEquals(JDBC_DRIVER_CLASS_NAME, actual.getDriverClassName());
assertEquals(USERNAME, actual.getUsername());
assertEquals(PASSWORD, actual.getPassword());
assertEquals(CONNECTION_TIMEOUT.longValue(), actual.getConnectionTimeout());
assertEquals(DataSourcePoolProperties.DEFAULT_VALIDATION_TIMEOUT, actual.getValidationTimeout());
assertEquals(DataSourcePoolProperties.DEFAULT_IDLE_TIMEOUT, actual.getIdleTimeout());
assertEquals(MAX_POOL_SIZE.intValue(), actual.getMaximumPoolSize());
assertEquals(DataSourcePoolProperties.DEFAULT_MINIMUM_IDLE, actual.getMinimumIdle());
}
}

View File

@ -0,0 +1,57 @@
/*
* Copyright 1999-2023 Alibaba Group Holding Ltd.
*
* 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 com.alibaba.nacos.persistence.datasource;
import com.alibaba.nacos.persistence.configuration.DatasourceConfiguration;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.test.util.ReflectionTestUtils;
@RunWith(MockitoJUnitRunner.class)
public class DynamicDataSourceTest {
@InjectMocks
private DynamicDataSource dataSource;
@Mock
private LocalDataSourceServiceImpl localDataSourceService;
@Mock
private ExternalDataSourceServiceImpl basicDataSourceService;
@Before
public void setUp() {
dataSource = DynamicDataSource.getInstance();
ReflectionTestUtils.setField(dataSource, "localDataSourceService", localDataSourceService);
ReflectionTestUtils.setField(dataSource, "basicDataSourceService", basicDataSourceService);
}
@Test
public void testGetDataSource() {
DatasourceConfiguration.setEmbeddedStorage(true);
Assert.assertTrue(dataSource.getDataSource() instanceof LocalDataSourceServiceImpl);
DatasourceConfiguration.setEmbeddedStorage(false);
Assert.assertTrue(dataSource.getDataSource() instanceof ExternalDataSourceServiceImpl);
}
}

View File

@ -0,0 +1,144 @@
/*
* Copyright 1999-2023 Alibaba Group Holding Ltd.
*
* 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 com.alibaba.nacos.persistence.datasource;
import com.zaxxer.hikari.HikariDataSource;
import org.junit.Assert;
import org.junit.Test;
import org.springframework.mock.env.MockEnvironment;
import java.util.List;
public class ExternalDataSourcePropertiesTest {
@SuppressWarnings("checkstyle:linelength")
public static final String JDBC_URL = "jdbc:mysql://127.0.0.1:3306/nacos_devtest?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC";
public static final String PASSWORD = "nacos";
public static final String USERNAME = "nacos_devtest";
@Test
public void externalDatasourceNormally() {
HikariDataSource expectedDataSource = new HikariDataSource();
expectedDataSource.setJdbcUrl(JDBC_URL);
expectedDataSource.setUsername(USERNAME);
expectedDataSource.setPassword(PASSWORD);
MockEnvironment environment = new MockEnvironment();
environment.setProperty("db.num", "1");
environment.setProperty("db.user", USERNAME);
environment.setProperty("db.password", PASSWORD);
environment.setProperty("db.url.0", JDBC_URL);
List<HikariDataSource> dataSources = new ExternalDataSourceProperties().build(environment, (dataSource -> {
Assert.assertEquals(dataSource.getJdbcUrl(), expectedDataSource.getJdbcUrl());
Assert.assertEquals(dataSource.getUsername(), expectedDataSource.getUsername());
Assert.assertEquals(dataSource.getPassword(), expectedDataSource.getPassword());
}));
Assert.assertEquals(dataSources.size(), 1);
}
@Test
public void externalDatasourceToAssertMultiJdbcUrl() {
HikariDataSource expectedDataSource = new HikariDataSource();
expectedDataSource.setJdbcUrl(JDBC_URL);
expectedDataSource.setUsername(USERNAME);
expectedDataSource.setPassword(PASSWORD);
MockEnvironment environment = new MockEnvironment();
environment.setProperty("db.num", "2");
environment.setProperty("db.user", USERNAME);
environment.setProperty("db.password", PASSWORD);
environment.setProperty("db.url.0", JDBC_URL);
environment.setProperty("db.url.1", JDBC_URL);
List<HikariDataSource> dataSources = new ExternalDataSourceProperties().build(environment, (dataSource -> {
Assert.assertEquals(dataSource.getJdbcUrl(), expectedDataSource.getJdbcUrl());
Assert.assertEquals(dataSource.getUsername(), expectedDataSource.getUsername());
Assert.assertEquals(dataSource.getPassword(), expectedDataSource.getPassword());
}));
Assert.assertEquals(dataSources.size(), 2);
}
@Test
public void externalDatasourceToAssertMultiPasswordAndUsername() {
HikariDataSource expectedDataSource = new HikariDataSource();
expectedDataSource.setJdbcUrl(JDBC_URL);
expectedDataSource.setUsername(USERNAME);
expectedDataSource.setPassword(PASSWORD);
MockEnvironment environment = new MockEnvironment();
environment.setProperty("db.num", "2");
environment.setProperty("db.user.0", USERNAME);
environment.setProperty("db.user.1", USERNAME);
environment.setProperty("db.password.0", PASSWORD);
environment.setProperty("db.password.1", PASSWORD);
environment.setProperty("db.url.0", JDBC_URL);
environment.setProperty("db.url.1", JDBC_URL);
List<HikariDataSource> dataSources = new ExternalDataSourceProperties().build(environment, (dataSource -> {
Assert.assertEquals(dataSource.getJdbcUrl(), expectedDataSource.getJdbcUrl());
Assert.assertEquals(dataSource.getUsername(), expectedDataSource.getUsername());
Assert.assertEquals(dataSource.getPassword(), expectedDataSource.getPassword());
}));
Assert.assertEquals(dataSources.size(), 2);
}
@Test
public void externalDatasourceToAssertMinIdle() {
MockEnvironment environment = new MockEnvironment();
environment.setProperty("db.num", "1");
environment.setProperty("db.user", USERNAME);
environment.setProperty("db.password", PASSWORD);
environment.setProperty("db.url.0", JDBC_URL);
List<HikariDataSource> dataSources = new ExternalDataSourceProperties().build(environment, (dataSource -> {
dataSource.validate();
Assert.assertEquals(dataSource.getMinimumIdle(), DataSourcePoolProperties.DEFAULT_MINIMUM_IDLE);
}));
Assert.assertEquals(dataSources.size(), 1);
}
@Test(expected = IllegalArgumentException.class)
public void externalDatasourceFailureWithLarkInfo() {
MockEnvironment environment = new MockEnvironment();
new ExternalDataSourceProperties().build(environment, null);
}
@Test(expected = IllegalArgumentException.class)
public void externalDatasourceFailureWithErrorInfo() {
HikariDataSource expectedDataSource = new HikariDataSource();
expectedDataSource.setJdbcUrl(JDBC_URL);
expectedDataSource.setUsername(USERNAME);
expectedDataSource.setPassword(PASSWORD);
MockEnvironment environment = new MockEnvironment();
// error num of db
environment.setProperty("db.num", "2");
environment.setProperty("db.user", USERNAME);
environment.setProperty("db.password", PASSWORD);
environment.setProperty("db.url.0", JDBC_URL);
List<HikariDataSource> dataSources = new ExternalDataSourceProperties().build(environment, (dataSource -> {
Assert.assertEquals(dataSource.getJdbcUrl(), expectedDataSource.getJdbcUrl());
Assert.assertEquals(dataSource.getUsername(), expectedDataSource.getUsername());
Assert.assertEquals(dataSource.getPassword(), expectedDataSource.getPassword());
}));
}
}

View File

@ -0,0 +1,150 @@
/*
* Copyright 1999-2023 Alibaba Group Holding Ltd.
*
* 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 com.alibaba.nacos.persistence.datasource;
import com.zaxxer.hikari.HikariDataSource;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.UncategorizedSQLException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.transaction.support.TransactionTemplate;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.class)
public class ExternalDataSourceServiceImplTest {
@InjectMocks
private ExternalDataSourceServiceImpl service;
@Mock
private JdbcTemplate jt;
@Mock
private DataSourceTransactionManager tm;
@Mock
private TransactionTemplate tjt;
@Mock
private JdbcTemplate testMasterJT;
@Mock
private JdbcTemplate testMasterWritableJT;
@Before
public void setUp() {
service = new ExternalDataSourceServiceImpl();
ReflectionTestUtils.setField(service, "jt", jt);
ReflectionTestUtils.setField(service, "tm", tm);
ReflectionTestUtils.setField(service, "tjt", tjt);
ReflectionTestUtils.setField(service, "testMasterJT", testMasterJT);
ReflectionTestUtils.setField(service, "testMasterWritableJT", testMasterWritableJT);
List<HikariDataSource> dataSourceList = new ArrayList<>();
dataSourceList.add(new HikariDataSource());
ReflectionTestUtils.setField(service, "dataSourceList", dataSourceList);
}
@Test
public void testCheckMasterWritable() {
when(testMasterWritableJT.queryForObject(eq(" SELECT @@read_only "), eq(Integer.class))).thenReturn(0);
Assert.assertTrue(service.checkMasterWritable());
}
@Test
public void testGetCurrentDbUrl() {
HikariDataSource bds = new HikariDataSource();
bds.setJdbcUrl("test.jdbc.url");
when(jt.getDataSource()).thenReturn(bds);
Assert.assertEquals("test.jdbc.url", service.getCurrentDbUrl());
}
@Test
public void testGetHealth() {
List<Boolean> isHealthList = new ArrayList<>();
ReflectionTestUtils.setField(service, "isHealthList", isHealthList);
Assert.assertEquals("UP", service.getHealth());
}
@Test
public void testCheckDbHealthTaskRun() {
List<JdbcTemplate> testJtList = new ArrayList<>();
testJtList.add(jt);
ReflectionTestUtils.setField(service, "testJtList", testJtList);
List<Boolean> isHealthList = new ArrayList<>();
isHealthList.add(Boolean.FALSE);
ReflectionTestUtils.setField(service, "isHealthList", isHealthList);
service.new CheckDbHealthTask().run();
Assert.assertEquals(1, isHealthList.size());
Assert.assertTrue(isHealthList.get(0));
}
@Test
public void testCheckDbHealthTaskRunWhenEmptyResult() {
List<JdbcTemplate> testJtList = new ArrayList<>();
testJtList.add(jt);
ReflectionTestUtils.setField(service, "testJtList", testJtList);
List<Boolean> isHealthList = new ArrayList<>();
isHealthList.add(Boolean.FALSE);
ReflectionTestUtils.setField(service, "isHealthList", isHealthList);
when(jt.queryForMap(anyString())).thenThrow(new EmptyResultDataAccessException("Expected exception", 1));
service.new CheckDbHealthTask().run();
Assert.assertEquals(1, isHealthList.size());
Assert.assertTrue(isHealthList.get(0));
}
@Test
public void testCheckDbHealthTaskRunWhenSqlException() {
List<JdbcTemplate> testJtList = new ArrayList<>();
testJtList.add(jt);
ReflectionTestUtils.setField(service, "testJtList", testJtList);
List<Boolean> isHealthList = new ArrayList<>();
isHealthList.add(Boolean.FALSE);
ReflectionTestUtils.setField(service, "isHealthList", isHealthList);
when(jt.queryForMap(anyString())).thenThrow(
new UncategorizedSQLException("Expected exception", "", new SQLException()));
service.new CheckDbHealthTask().run();
Assert.assertEquals(1, isHealthList.size());
Assert.assertFalse(isHealthList.get(0));
}
}

View File

@ -0,0 +1,76 @@
/*
* Copyright 1999-2023 Alibaba Group Holding Ltd.
*
* 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 com.alibaba.nacos.persistence.datasource;
import com.zaxxer.hikari.HikariDataSource;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.transaction.support.TransactionTemplate;
import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.class)
public class LocalDataSourceServiceImplTest {
@InjectMocks
private LocalDataSourceServiceImpl service;
@Mock
private JdbcTemplate jt;
@Mock
private TransactionTemplate tjt;
@Before
public void setUp() {
service = new LocalDataSourceServiceImpl();
ReflectionTestUtils.setField(service, "jt", jt);
ReflectionTestUtils.setField(service, "tjt", tjt);
}
@Test
public void testGetDataSource() {
HikariDataSource dataSource = new HikariDataSource();
dataSource.setJdbcUrl("test.jdbc.url");
when(jt.getDataSource()).thenReturn(dataSource);
Assert.assertEquals(dataSource.getJdbcUrl(), ((HikariDataSource) service.getDatasource()).getJdbcUrl());
}
@Test
public void testCheckMasterWritable() {
Assert.assertTrue(service.checkMasterWritable());
}
@Test
public void testSetAndGetHealth() {
service.setHealthStatus("DOWN");
Assert.assertEquals("DOWN", service.getHealth());
service.setHealthStatus("UP");
Assert.assertEquals("UP", service.getHealth());
}
}

View File

@ -0,0 +1,132 @@
/*
* Copyright 1999-2023 Alibaba Group Holding Ltd.
*
* 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 com.alibaba.nacos.persistence.datasource;
import com.alibaba.nacos.persistence.configuration.DatasourceConfiguration;
import com.alibaba.nacos.persistence.constants.PersistenceConstant;
import com.alibaba.nacos.sys.env.Constants;
import com.alibaba.nacos.sys.env.EnvUtil;
import org.junit.Assert;
import org.junit.Before;
import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.MethodSorters;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.mock.env.MockEnvironment;
import org.springframework.test.util.ReflectionTestUtils;
/**
* StandaloneExternalStorage unit test.
*
* @author Long Yu
* @since 2.2.0
*/
@RunWith(MockitoJUnitRunner.class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class StandaloneExternalStorageTest {
@InjectMocks
private DynamicDataSource dataSource;
private MockEnvironment environment;
@Mock
private LocalDataSourceServiceImpl localDataSourceService;
@Mock
private ExternalDataSourceServiceImpl basicDataSourceService;
DatasourceConfiguration datasourceConfig;
@Before
public void setUp() throws Exception {
environment = new MockEnvironment();
EnvUtil.setEnvironment(environment);
datasourceConfig = new DatasourceConfiguration();
dataSource = DynamicDataSource.getInstance();
ReflectionTestUtils.setField(dataSource, "localDataSourceService", localDataSourceService);
ReflectionTestUtils.setField(dataSource, "basicDataSourceService", basicDataSourceService);
}
@Test
public void test001WithStandaloneAndNullDatabase() {
// 模拟设置环境01指定单例未指定数据库UseExternalDB是false
System.setProperty(Constants.STANDALONE_MODE_PROPERTY_NAME, "true");
environment.setProperty(PersistenceConstant.DATASOURCE_PLATFORM_PROPERTY_OLD, "");
EnvUtil.setIsStandalone(Boolean.getBoolean(Constants.STANDALONE_MODE_PROPERTY_NAME));
DatasourceConfiguration.setEmbeddedStorage(EnvUtil.getStandaloneMode());
// 模拟初始化
datasourceConfig.initialize(null);
Assert.assertTrue(EnvUtil.getStandaloneMode());
Assert.assertTrue(dataSource.getDataSource() instanceof LocalDataSourceServiceImpl);
Assert.assertFalse(DatasourceConfiguration.isUseExternalDB());
}
@Test
public void test002WithStandaloneAndDerbyDatabase() {
// 模拟设置环境02指定单例指定数据库derbyUseExternalDB是false
System.setProperty(Constants.STANDALONE_MODE_PROPERTY_NAME, "true");
environment.setProperty(PersistenceConstant.DATASOURCE_PLATFORM_PROPERTY_OLD, "derby");
EnvUtil.setIsStandalone(Boolean.getBoolean(Constants.STANDALONE_MODE_PROPERTY_NAME));
DatasourceConfiguration.setEmbeddedStorage(EnvUtil.getStandaloneMode());
// 模拟初始化
datasourceConfig.initialize(null);
Assert.assertTrue(EnvUtil.getStandaloneMode());
Assert.assertTrue(dataSource.getDataSource() instanceof LocalDataSourceServiceImpl);
Assert.assertFalse(DatasourceConfiguration.isUseExternalDB());
}
@Test
public void test003WithStandaloneAndMysqlDatabase() {
// 模拟设置环境03指定单例指定数据库为mysql UseExternalDB是true
System.setProperty(Constants.STANDALONE_MODE_PROPERTY_NAME, "true");
environment.setProperty(PersistenceConstant.DATASOURCE_PLATFORM_PROPERTY_OLD, "mysql");
EnvUtil.setIsStandalone(Boolean.getBoolean(Constants.STANDALONE_MODE_PROPERTY_NAME));
DatasourceConfiguration.setEmbeddedStorage(EnvUtil.getStandaloneMode());
// 模拟初始化
datasourceConfig.initialize(null);
Assert.assertTrue(EnvUtil.getStandaloneMode());
Assert.assertTrue(dataSource.getDataSource() instanceof ExternalDataSourceServiceImpl);
Assert.assertTrue(DatasourceConfiguration.isUseExternalDB());
}
@Test
public void test004WithStandaloneAndOtherDatabase() {
// 模拟设置环境04指定单例指定数据库为其他 UseExternalDB是true
System.setProperty(Constants.STANDALONE_MODE_PROPERTY_NAME, "true");
environment.setProperty(PersistenceConstant.DATASOURCE_PLATFORM_PROPERTY_OLD, "postgresql");
EnvUtil.setIsStandalone(Boolean.getBoolean(Constants.STANDALONE_MODE_PROPERTY_NAME));
DatasourceConfiguration.setEmbeddedStorage(EnvUtil.getStandaloneMode());
// 模拟初始化
datasourceConfig.initialize(null);
Assert.assertTrue(EnvUtil.getStandaloneMode());
Assert.assertTrue(dataSource.getDataSource() instanceof ExternalDataSourceServiceImpl);
Assert.assertTrue(DatasourceConfiguration.isUseExternalDB());
}
}

View File

@ -0,0 +1,80 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* 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 com.alibaba.nacos.persistence.repository.embedded.operate;
import java.util.Objects;
public class MockConfigInfo {
private long id;
private String dataId;
private String group;
private String content;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getDataId() {
return dataId;
}
public void setDataId(String dataId) {
this.dataId = dataId;
}
public String getGroup() {
return group;
}
public void setGroup(String group) {
this.group = group;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof MockConfigInfo)) {
return false;
}
MockConfigInfo that = (MockConfigInfo) o;
return id == that.id && Objects.equals(dataId, that.dataId) && Objects.equals(group, that.group) && Objects
.equals(content, that.content);
}
@Override
public int hashCode() {
return Objects.hash(id, dataId, group, content);
}
}

View File

@ -0,0 +1,352 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* 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 com.alibaba.nacos.persistence.repository.embedded.operate;
import com.alibaba.nacos.common.model.RestResult;
import com.alibaba.nacos.persistence.repository.embedded.EmbeddedStorageContextHolder;
import com.alibaba.nacos.persistence.repository.embedded.sql.ModifyRequest;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Spy;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.function.BiConsumer;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.class)
public class StandaloneDatabaseOperateImplTest {
@Spy
@InjectMocks
private StandaloneDatabaseOperateImpl operate;
@Mock
private RowMapper<MockConfigInfo> rowMapper;
@Mock
private JdbcTemplate jdbcTemplate;
@Mock
private JdbcTemplate tempJdbcTemplate;
@Mock
private BiConsumer<Boolean, Throwable> biConsumer;
/* @Mock
private File file;
*/
@Mock
private TransactionTemplate transactionTemplate;
@Before
public void setUp() {
ReflectionTestUtils.setField(operate, "jdbcTemplate", jdbcTemplate);
ReflectionTestUtils.setField(operate, "transactionTemplate", transactionTemplate);
}
@Test
public void testQueryOne1() {
String sql = "SELECT 1";
Class<Long> clazz = Long.class;
Long num = 1L;
when(jdbcTemplate.queryForObject(sql, clazz)).thenReturn(num);
Assert.assertEquals(operate.queryOne(sql, clazz), (Long) 1L);
}
@Test
public void testQueryOne2() {
final String sql = "SELECT * FROM config_info WHERE id = ? AND data_id = ? AND group_id = ?";
MockConfigInfo configInfo = new MockConfigInfo();
configInfo.setId(1L);
configInfo.setDataId("test");
configInfo.setGroup("test");
Object[] args = new Object[] {configInfo.getId(), configInfo.getDataId(), configInfo.getGroup()};
when(jdbcTemplate.queryForObject(sql, args, MockConfigInfo.class)).thenReturn(configInfo);
Assert.assertEquals(operate.queryOne(sql, args, MockConfigInfo.class), configInfo);
}
@Test
public void testQueryOne3() {
final String sql = "SELECT * FROM config_info WHERE id = ? AND data_id = ? AND group_id = ?";
MockConfigInfo configInfo = new MockConfigInfo();
configInfo.setId(1L);
configInfo.setDataId("test");
configInfo.setGroup("test");
Object[] args = new Object[] {configInfo.getId(), configInfo.getDataId(), configInfo.getGroup()};
when(jdbcTemplate.queryForObject(eq(sql), eq(args), any(RowMapper.class))).thenReturn(configInfo);
Assert.assertEquals(operate.queryOne(sql, args, rowMapper), configInfo);
}
@Test
public void testQueryOne4() {
String sql = "SELECT 1";
Class<Long> clazz = Long.class;
Long result = 1L;
when(tempJdbcTemplate.queryForObject(sql, clazz)).thenReturn(result);
Assert.assertEquals(operate.queryOne(tempJdbcTemplate, sql, clazz), result);
}
@Test
public void testQueryOne5() {
final String sql = "SELECT * FROM config_info WHERE id = ? AND data_id = ? AND group_id = ?";
MockConfigInfo configInfo = new MockConfigInfo();
configInfo.setId(1L);
configInfo.setDataId("test");
configInfo.setGroup("test");
Object[] args = new Object[] {configInfo.getId(), configInfo.getDataId(), configInfo.getGroup()};
when(tempJdbcTemplate.queryForObject(sql, args, MockConfigInfo.class)).thenReturn(configInfo);
Assert.assertEquals(operate.queryOne(tempJdbcTemplate, sql, args, MockConfigInfo.class), configInfo);
}
@Test
public void testQueryOne6() {
final String sql = "SELECT * FROM config_info WHERE id = ? AND data_id = ? AND group_id = ?";
MockConfigInfo configInfo = new MockConfigInfo();
configInfo.setId(1L);
configInfo.setDataId("test");
configInfo.setGroup("test");
Object[] args = new Object[] {configInfo.getId(), configInfo.getDataId(), configInfo.getGroup()};
when(tempJdbcTemplate.queryForObject(eq(sql), eq(args), any(RowMapper.class))).thenReturn(configInfo);
Assert.assertEquals(operate.queryOne(tempJdbcTemplate, sql, args, rowMapper), configInfo);
}
@Test
public void testQueryMany1() {
final String sql = "SELECT * FROM config_info WHERE id >= ? AND id <= ?";
final Object[] args = new Object[] {1, 2};
MockConfigInfo configInfo1 = new MockConfigInfo();
configInfo1.setId(1);
MockConfigInfo configInfo2 = new MockConfigInfo();
configInfo2.setId(2);
List<MockConfigInfo> configInfos = new ArrayList<>();
configInfos.add(configInfo1);
configInfos.add(configInfo2);
when(jdbcTemplate.query(eq(sql), eq(args), any(RowMapper.class))).thenReturn(configInfos);
Assert.assertEquals(configInfos, operate.queryMany(sql, args, rowMapper));
}
@Test
public void testQueryMany2() {
final String sql = "SELECT id, data_id, group_id FROM config_info WHERE id >= ? AND id <= ?";
final Object[] args = new Object[] {1, 2};
final List<Map<String, Object>> resultList = new ArrayList<>();
Map<String, Object> map1 = new HashMap<>();
map1.put("id", 1);
map1.put("data_id", "test");
map1.put("group_id", "test");
final Map<String, Object> map2 = new HashMap<>();
map1.put("id", 2);
map1.put("data_id", "test");
map1.put("group_id", "test");
resultList.add(map1);
resultList.add(map2);
when(jdbcTemplate.queryForList(sql, args)).thenReturn(resultList);
Assert.assertEquals(operate.queryMany(sql, args), resultList);
}
@Test
public void testQueryMany3() {
String sql = "SELECT data_id FROM config_info WHERE id >= ? AND id <= ?";
Object[] args = new Object[] {1, 2};
String dataId1 = "test1";
String dataId2 = "test2";
List<String> resultList = new ArrayList<>();
resultList.add(dataId1);
resultList.add(dataId2);
Class clazz = dataId1.getClass();
when(jdbcTemplate.queryForList(sql, args, clazz)).thenReturn(resultList);
Assert.assertEquals(operate.queryMany(sql, args, clazz), resultList);
}
@Test
public void testQueryMany4() {
final String sql = "SELECT data_id FROM config_info WHERE id >= ? AND id <= ?";
final Object[] args = new Object[] {1, 2};
final List<Map<String, Object>> resultList = new ArrayList<>();
Map<String, Object> map1 = new HashMap<>();
map1.put("id", 1);
map1.put("data_id", "test");
map1.put("group_id", "test");
final Map<String, Object> map2 = new HashMap<>();
map1.put("id", 2);
map1.put("data_id", "test");
map1.put("group_id", "test");
resultList.add(map1);
resultList.add(map2);
when(tempJdbcTemplate.queryForList(sql, args)).thenReturn(resultList);
Assert.assertEquals(operate.queryMany(tempJdbcTemplate, sql, args), resultList);
}
@Test
public void testQueryMany5() {
String sql = "SELECT data_id FROM config_info WHERE id >= ? AND id <= ?";
Object[] args = new Object[] {1, 2};
String dataId1 = "test1";
String dataId2 = "test2";
List<String> resultList = new ArrayList<>();
resultList.add(dataId1);
resultList.add(dataId2);
Class clazz = dataId1.getClass();
when(operate.queryMany(jdbcTemplate, sql, args, clazz)).thenReturn(resultList);
Assert.assertEquals(operate.queryMany(jdbcTemplate, sql, args, clazz), resultList);
}
@Test
public void testQueryMany6() {
final String sql = "SELECT * FROM config_info WHERE id >= ? AND id <= ?";
final Object[] args = new Object[] {1, 2};
MockConfigInfo configInfo1 = new MockConfigInfo();
configInfo1.setId(1);
MockConfigInfo configInfo2 = new MockConfigInfo();
configInfo2.setId(2);
List<MockConfigInfo> configInfos = new ArrayList<>();
configInfos.add(configInfo1);
configInfos.add(configInfo2);
when(tempJdbcTemplate.query(eq(sql), eq(args), any(RowMapper.class))).thenReturn(configInfos);
Assert.assertEquals(operate.queryMany(tempJdbcTemplate, sql, args, rowMapper), configInfos);
}
@Test
public void testDataImport() throws ExecutionException, InterruptedException {
RestResult<String> errorResult = RestResult.<String>builder().withCode(500).withMsg("null").withData(null)
.build();
CompletableFuture<RestResult<String>> errorFuture = new CompletableFuture<>();
errorFuture.complete(errorResult);
doReturn(errorFuture).when(operate).dataImport(null);
Assert.assertEquals(operate.dataImport(null).get(), errorResult);
}
@Test
public void testUpdate1() {
List<ModifyRequest> modifyRequests = new ArrayList<>();
ModifyRequest modifyRequest1 = new ModifyRequest();
String sql = "UPDATE config_info SET data_id = 'test' WHERE id = ?;";
modifyRequest1.setSql(sql);
Object[] args = new Object[] {1};
modifyRequest1.setArgs(args);
modifyRequests.add(modifyRequest1);
when(transactionTemplate.execute(any(TransactionCallback.class))).thenReturn(true);
Assert.assertTrue(operate.update(modifyRequests));
}
@Test
public void testUpdate2() {
List<ModifyRequest> modifyRequests = new ArrayList<>();
ModifyRequest modifyRequest1 = new ModifyRequest();
String sql = "UPDATE config_info SET data_id = 'test' WHERE id = ?;";
modifyRequest1.setSql(sql);
Object[] args = new Object[] {1};
modifyRequest1.setArgs(args);
modifyRequests.add(modifyRequest1);
when(transactionTemplate.execute(any(TransactionCallback.class))).thenReturn(true);
Assert.assertTrue(operate.update(modifyRequests, biConsumer));
}
@Test
public void testUpdate3() {
List<ModifyRequest> modifyRequests = new ArrayList<>();
ModifyRequest modifyRequest1 = new ModifyRequest();
String sql = "UPDATE config_info SET data_id = 'test' WHERE id = ?;";
modifyRequest1.setSql(sql);
Object[] args = new Object[] {1};
modifyRequest1.setArgs(args);
modifyRequests.add(modifyRequest1);
when(transactionTemplate.execute(any(TransactionCallback.class))).thenReturn(true);
Assert.assertTrue(operate.update(transactionTemplate, jdbcTemplate, modifyRequests));
}
@Test
public void testUpdate4() {
List<ModifyRequest> modifyRequests = new ArrayList<>();
ModifyRequest modifyRequest1 = new ModifyRequest();
String sql = "UPDATE config_info SET data_id = 'test' WHERE id = ?;";
modifyRequest1.setSql(sql);
Object[] args = new Object[] {1};
modifyRequest1.setArgs(args);
modifyRequests.add(modifyRequest1);
when(transactionTemplate.execute(any(TransactionCallback.class))).thenReturn(true);
Assert.assertTrue(operate.update(transactionTemplate, jdbcTemplate, modifyRequests, biConsumer));
}
@Test
public void testBlockUpdate1() {
String sql = "UPDATE config_info SET data_id = 'test' WHERE id = 1;";
EmbeddedStorageContextHolder.addSqlContext(sql);
when(transactionTemplate.execute(any(TransactionCallback.class))).thenReturn(true);
Assert.assertTrue(operate.blockUpdate());
}
@Test
public void testBlockUpdate2() {
String sql = "UPDATE config_info SET data_id = 'test' WHERE id = 1;";
EmbeddedStorageContextHolder.addSqlContext(sql);
when(transactionTemplate.execute(any(TransactionCallback.class))).thenReturn(true);
Assert.assertTrue(operate.blockUpdate(biConsumer));
}
@Test
public void testDoDataImport() {
List<ModifyRequest> modifyRequests = new ArrayList<>();
ModifyRequest modifyRequest1 = new ModifyRequest();
String sql = "UPDATE config_info SET data_id = 'test' WHERE id = ?;";
modifyRequest1.setSql(sql);
Object[] args = new Object[] {1};
modifyRequest1.setArgs(args);
modifyRequests.add(modifyRequest1);
when(tempJdbcTemplate.batchUpdate(sql)).thenReturn(new int[] {1});
Assert.assertTrue(operate.doDataImport(tempJdbcTemplate, modifyRequests));
}
@Test
public void testFutureUpdate() throws ExecutionException, InterruptedException {
String sql = "SELECT 1";
EmbeddedStorageContextHolder.addSqlContext(sql);
CompletableFuture<Boolean> future = new CompletableFuture<>();
future.complete(true);
doAnswer((invocationOnMock) -> null).when(operate).futureUpdate();
when(operate.futureUpdate()).thenReturn(future);
Assert.assertTrue(operate.futureUpdate().get());
}
}

View File

@ -0,0 +1,54 @@
/*
* Copyright 1999-2023 Alibaba Group Holding Ltd.
*
* 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 com.alibaba.nacos.persistence.utils;
import com.zaxxer.hikari.HikariDataSource;
import org.junit.Test;
import java.sql.Connection;
import java.sql.SQLException;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
/**
* DataSource Connection CheckUtil Unit Test.
*
* @author Long Yu
*/
public class ConnectionCheckUtilTest {
@Test(expected = RuntimeException.class)
public void testCheckConnectionThrowException() throws SQLException {
HikariDataSource ds = mock(HikariDataSource.class);
when(ds.getConnection()).thenThrow(new RuntimeException());
ConnectionCheckUtil.checkDataSourceConnection(ds);
verify(ds).getConnection();
}
@Test
public void testCheckConnectionNormal() throws SQLException {
HikariDataSource ds = mock(HikariDataSource.class);
Connection connection = mock(Connection.class);
when(ds.getConnection()).thenReturn(connection);
ConnectionCheckUtil.checkDataSourceConnection(ds);
verify(ds).getConnection();
verify(connection).close();
}
}

View File

@ -0,0 +1,34 @@
/*
* Copyright 1999-2023 Alibaba Group Holding Ltd.
*
* 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 com.alibaba.nacos.persistence.utils;
import org.junit.Assert;
import org.junit.Test;
@SuppressWarnings("checkstyle:linelength")
public class DerbyUtilsTest {
@Test
public void testDerbySqlCorrect() {
final String testSql = "INSERT INTO `config_info` (`id`, `data_id`, `group_id`, `content`, `md5`, `gmt_create`, `gmt_modified`, `src_user`, `src_ip`, `app_name`, `tenant_id`, `c_desc`, `c_use`, `effect`, `type`, `c_schema`) VALUES (1,'boot-test','ALIBABA','dept:123123123\\ngroup:123123123','2ca50d002a7dabf81497f666a7967e15','2020-04-13 13:44:43','2020-04-30 10:45:21',NULL,'127.0.0.1','','',NULL,NULL,NULL,NULL,NULL);";
final String result = DerbyUtils.insertStatementCorrection(testSql);
final String expect = "INSERT INTO CONFIG_INFO (ID, DATA_ID, GROUP_ID, CONTENT, MD5, GMT_CREATE, GMT_MODIFIED, SRC_USER, SRC_IP, APP_NAME, TENANT_ID, C_DESC, C_USE, EFFECT, TYPE, C_SCHEMA) VALUES (1,'boot-test','ALIBABA','dept:123123123\\ngroup:123123123','2ca50d002a7dabf81497f666a7967e15','2020-04-13 13:44:43','2020-04-30 10:45:21',NULL,'127.0.0.1','','',NULL,NULL,NULL,NULL,NULL)";
Assert.assertEquals(expect, result);
}
}

View File

@ -0,0 +1,42 @@
/*
* Copyright 1999-2023 Alibaba Group Holding Ltd.
*
* 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 com.alibaba.nacos.persistence.utils;
import org.junit.Assert;
import org.junit.Test;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
public class PersistenceExecutorTest {
@Test
public void testExecuteEmbeddedDump() throws InterruptedException {
AtomicInteger atomicInteger = new AtomicInteger();
Runnable runnable = atomicInteger::incrementAndGet;
PersistenceExecutor.executeEmbeddedDump(runnable);
TimeUnit.MILLISECONDS.sleep(20);
Assert.assertEquals(1, atomicInteger.get());
}
}

View File

@ -0,0 +1,15 @@
#
# Copyright 1999-2023 Alibaba Group Holding Ltd.
#
# 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.
#