feat: add tdengine jdbc history writer
This commit is contained in:
@@ -0,0 +1,147 @@
|
||||
package com.lingniu.ingest.tdenginehistory;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.io.IOException;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.sql.Timestamp;
|
||||
import java.time.Instant;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
|
||||
public final class TdengineJdbcHistoryWriter implements TdengineHistoryWriter {
|
||||
|
||||
private final DataSource dataSource;
|
||||
private final TdengineHistorySchema schema;
|
||||
private final TdengineHistoryStatements statements;
|
||||
|
||||
public TdengineJdbcHistoryWriter(DataSource dataSource, TdengineHistorySchema schema) {
|
||||
if (dataSource == null) {
|
||||
throw new IllegalArgumentException("dataSource must not be null");
|
||||
}
|
||||
if (schema == null) {
|
||||
throw new IllegalArgumentException("schema must not be null");
|
||||
}
|
||||
this.dataSource = dataSource;
|
||||
this.schema = schema;
|
||||
this.statements = new TdengineHistoryStatements(schema);
|
||||
}
|
||||
|
||||
public void initializeSchema() throws IOException {
|
||||
inTransaction(connection -> {
|
||||
try (Statement statement = connection.createStatement()) {
|
||||
for (String sql : schema.bootstrapSql()) {
|
||||
statement.execute(sql);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendRawFrames(List<TdengineRawFrameRow> rows) throws IOException {
|
||||
append(rows, statements::rawFrame);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendLocations(List<TdengineLocationRow> rows) throws IOException {
|
||||
append(rows, statements::location);
|
||||
}
|
||||
|
||||
private <T> void append(List<T> rows, Function<T, TdengineBatchStatement> mapper) throws IOException {
|
||||
if (rows == null || rows.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
inTransaction(connection -> {
|
||||
Set<String> createdChildTables = new LinkedHashSet<>();
|
||||
Map<String, PreparedStatement> preparedStatements = new LinkedHashMap<>();
|
||||
try {
|
||||
for (T row : rows) {
|
||||
if (row == null) {
|
||||
continue;
|
||||
}
|
||||
TdengineBatchStatement batch = mapper.apply(row);
|
||||
if (createdChildTables.add(batch.createChildTableSql())) {
|
||||
try (Statement statement = connection.createStatement()) {
|
||||
statement.execute(batch.createChildTableSql());
|
||||
}
|
||||
}
|
||||
PreparedStatement prepared = preparedStatements.computeIfAbsent(batch.insertSql(), sql -> {
|
||||
try {
|
||||
return connection.prepareStatement(sql);
|
||||
} catch (SQLException e) {
|
||||
throw new JdbcRuntimeException(e);
|
||||
}
|
||||
});
|
||||
bind(prepared, batch.values());
|
||||
prepared.addBatch();
|
||||
}
|
||||
for (PreparedStatement prepared : preparedStatements.values()) {
|
||||
prepared.executeBatch();
|
||||
}
|
||||
} finally {
|
||||
for (PreparedStatement prepared : preparedStatements.values()) {
|
||||
prepared.close();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static void bind(PreparedStatement prepared, List<Object> values) throws SQLException {
|
||||
for (int i = 0; i < values.size(); i++) {
|
||||
Object value = values.get(i);
|
||||
int parameterIndex = i + 1;
|
||||
if (value instanceof Instant instant) {
|
||||
prepared.setTimestamp(parameterIndex, Timestamp.from(instant));
|
||||
} else {
|
||||
prepared.setObject(parameterIndex, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void inTransaction(SqlWork work) throws IOException {
|
||||
try (Connection connection = dataSource.getConnection()) {
|
||||
boolean autoCommit = connection.getAutoCommit();
|
||||
connection.setAutoCommit(false);
|
||||
try {
|
||||
work.execute(connection);
|
||||
connection.commit();
|
||||
} catch (JdbcRuntimeException e) {
|
||||
rollback(connection);
|
||||
throw e.getCause();
|
||||
} catch (SQLException e) {
|
||||
rollback(connection);
|
||||
throw e;
|
||||
} finally {
|
||||
connection.setAutoCommit(autoCommit);
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
throw new IOException("tdengine history write failed", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void rollback(Connection connection) throws SQLException {
|
||||
connection.rollback();
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
private interface SqlWork {
|
||||
void execute(Connection connection) throws SQLException;
|
||||
}
|
||||
|
||||
private static final class JdbcRuntimeException extends RuntimeException {
|
||||
private JdbcRuntimeException(SQLException cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized SQLException getCause() {
|
||||
return (SQLException) super.getCause();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,18 @@
|
||||
package com.lingniu.ingest.tdenginehistory.config;
|
||||
|
||||
import com.lingniu.ingest.tdenginehistory.TdengineHistorySchema;
|
||||
import com.lingniu.ingest.tdenginehistory.TdengineHistoryStatements;
|
||||
import com.lingniu.ingest.tdenginehistory.TdengineHistoryWriter;
|
||||
import com.lingniu.ingest.tdenginehistory.TdengineJdbcHistoryWriter;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
@AutoConfiguration
|
||||
@EnableConfigurationProperties(TdengineHistoryProperties.class)
|
||||
public class TdengineHistoryAutoConfiguration {
|
||||
@@ -20,4 +26,25 @@ public class TdengineHistoryAutoConfiguration {
|
||||
public TdengineHistorySchema tdengineHistorySchema(TdengineHistoryProperties properties) {
|
||||
return new TdengineHistorySchema(properties.getDatabase());
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@ConditionalOnProperty(
|
||||
prefix = "lingniu.ingest.tdengine-history",
|
||||
name = "enabled",
|
||||
havingValue = "true")
|
||||
public TdengineHistoryStatements tdengineHistoryStatements(TdengineHistorySchema schema) {
|
||||
return new TdengineHistoryStatements(schema);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@ConditionalOnBean(DataSource.class)
|
||||
@ConditionalOnProperty(
|
||||
prefix = "lingniu.ingest.tdengine-history",
|
||||
name = "enabled",
|
||||
havingValue = "true")
|
||||
public TdengineHistoryWriter tdengineHistoryWriter(DataSource dataSource, TdengineHistorySchema schema) {
|
||||
return new TdengineJdbcHistoryWriter(dataSource, schema);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user