Search in sources :

Example 1 with SchemaUpdateResult

use of io.datarouter.storage.config.schema.SchemaUpdateResult in project datarouter by hotpads.

the class HBaseSchemaUpdateService method generateSchemaUpdate.

private Optional<SchemaUpdateResult> generateSchemaUpdate(ClientId clientId, Supplier<List<String>> existingTableNames, PhysicalNode<?, ?, ?> node) throws IOException {
    PhysicalDatabeanFieldInfo<?, ?, ?> fieldInfo = node.getFieldInfo();
    TableName tableName = TableName.valueOf(fieldInfo.getTableName());
    if (!existingTableNames.get().contains(tableName.getNameAsString())) {
        createTable(clientId, node);
        return Optional.empty();
    }
    Admin admin = hBaseConnectionHolder.getConnection(clientId).getAdmin();
    admin.getAlterStatus(tableName);
    HTableDescriptor desc = admin.getTableDescriptor(tableName);
    int requestedTtlSeconds = fieldInfo.getSampleFielder().getOption(TtlFielderConfig.KEY).map(TtlFielderConfig::getTtl).map(Duration::getSeconds).map(Math::toIntExact).orElse(HConstants.FOREVER);
    List<String> ddls = new ArrayList<>();
    for (HColumnDescriptor column : desc.getColumnFamilies()) {
        if (requestedTtlSeconds != column.getTimeToLive()) {
            String ddl = "alter '" + tableName + "', NAME => '" + column.getNameAsString() + "', TTL => " + requestedTtlSeconds;
            if (schemaUpdateOptions.getModifyTtl(false)) {
                logger.warn(SchemaUpdateTool.generateFullWidthMessage("Executing SchemaUpdate"));
                logger.warn(ddl);
                column.setTimeToLive(requestedTtlSeconds);
                admin.modifyColumn(tableName, column);
            } else if (schemaUpdateOptions.getModifyTtl(true)) {
                SchemaUpdateTool.printSchemaUpdate(logger, ddl);
                ddls.add(ddl);
            }
        }
        if (MAX_VERSIONS != column.getMaxVersions()) {
            String ddl = "alter '" + tableName + "', NAME => '" + column.getNameAsString() + "', VERSIONS => " + MAX_VERSIONS;
            if (schemaUpdateOptions.getModifyMaxVersions(false)) {
                logger.warn(SchemaUpdateTool.generateFullWidthMessage("Executing SchemaUpdate"));
                logger.warn(ddl);
                column.setMaxVersions(MAX_VERSIONS);
                admin.modifyColumn(tableName, column);
            } else if (schemaUpdateOptions.getModifyMaxVersions(true)) {
                SchemaUpdateTool.printSchemaUpdate(logger, ddl);
                ddls.add(ddl);
            }
        }
    }
    if (ddls.isEmpty()) {
        return Optional.empty();
    }
    return Optional.of(new SchemaUpdateResult(String.join("\n", ddls), null, clientId));
}
Also used : TableName(org.apache.hadoop.hbase.TableName) HColumnDescriptor(org.apache.hadoop.hbase.HColumnDescriptor) ArrayList(java.util.ArrayList) Duration(java.time.Duration) SchemaUpdateResult(io.datarouter.storage.config.schema.SchemaUpdateResult) Admin(org.apache.hadoop.hbase.client.Admin) HTableDescriptor(org.apache.hadoop.hbase.HTableDescriptor)

Example 2 with SchemaUpdateResult

use of io.datarouter.storage.config.schema.SchemaUpdateResult in project datarouter by hotpads.

the class MysqlSingleTableSchemaUpdateService method performSchemaUpdate.

public Optional<SchemaUpdateResult> performSchemaUpdate(ClientId clientId, Supplier<List<String>> existingTableNames, PhysicalNode<?, ?, ?> physicalNode) {
    MysqlConnectionPool connectionPool = mysqlConnectionPoolHolder.getConnectionPool(clientId);
    if (schemaUpdateOptions.getIgnoreClients().contains(clientId.getName()) || !clientId.getWritable()) {
        return Optional.empty();
    }
    String tableName = physicalNode.getFieldInfo().getTableName();
    String currentTableAbsoluteName = clientId.getName() + "." + tableName;
    if (schemaUpdateOptions.getIgnoreTables().contains(currentTableAbsoluteName)) {
        return Optional.empty();
    }
    String schemaName = connectionPool.getSchemaName();
    DatabeanFieldInfo<?, ?, ?> fieldInfo = physicalNode.getFieldInfo();
    List<Field<?>> primaryKeyFields = fieldInfo.getPrimaryKeyFields();
    List<Field<?>> nonKeyFields = fieldInfo.getNonKeyFields();
    Map<String, List<Field<?>>> indexes = Collections.emptyMap();
    Map<String, List<Field<?>>> uniqueIndexes = fieldInfo.getUniqueIndexes();
    MysqlTableOptions mysqlTableOptions = MysqlTableOptions.make(fieldInfo.getSampleFielder());
    MysqlCollation collation = mysqlTableOptions.getCollation();
    MysqlCharacterSet characterSet = mysqlTableOptions.getCharacterSet();
    MysqlRowFormat rowFormat = mysqlTableOptions.getRowFormat();
    if (physicalNode instanceof IndexedStorage) {
        IndexedStorage<?, ?> indexedStorage = (IndexedStorage<?, ?>) physicalNode;
        indexes = indexedStorage.getManagedNodes().stream().collect(Collectors.toMap(ManagedNode::getName, managedNode -> managedNode.getIndexEntryFieldInfo().getFields()));
    }
    SqlTable requested = fieldSqlTableGenerator.generate(tableName, primaryKeyFields, nonKeyFields, collation, characterSet, rowFormat, indexes, uniqueIndexes);
    boolean exists = existingTableNames.get().contains(tableName);
    if (!exists) {
        String createDdl = sqlCreateTableGenerator.generateDdl(requested, schemaName);
        if (schemaUpdateOptions.getCreateTables(false)) {
            logger.info(SchemaUpdateTool.generateFullWidthMessage("Creating the table " + tableName));
            logger.info(createDdl);
            try {
                MysqlTool.execute(connectionPool, createDdl);
                logger.info(SchemaUpdateTool.generateFullWidthMessage("Created " + tableName));
            } catch (RuntimeException e) {
                Throwable cause = e.getCause();
                if (!(cause instanceof SQLSyntaxErrorException)) {
                    throw e;
                }
                int errorCode = ((SQLSyntaxErrorException) e.getCause()).getErrorCode();
                if (errorCode != MysqlErrorNumbers.ER_TABLE_EXISTS_ERROR) {
                    throw e;
                }
                logger.warn(SchemaUpdateTool.generateFullWidthMessage("Did not create " + tableName + " because it already exists"));
            }
            return Optional.empty();
        }
        SchemaUpdateTool.printSchemaUpdate(logger, createDdl);
        return Optional.of(new SchemaUpdateResult(createDdl, tableName + " creation is required", clientId));
    }
    SqlTable executeCurrent = ConnectionSqlTableGenerator.generate(connectionPool, tableName, schemaName);
    SqlAlterTableGenerator executeAlterTableGenerator = sqlAlterTableGeneratorFactory.new SqlAlterTableGenerator(executeCurrent, requested, mysqlOptions.hostname(clientId), schemaName);
    // execute the alter table
    Ddl ddl = executeAlterTableGenerator.generateDdl();
    if (ddl.executeStatement.isPresent()) {
        PhaseTimer alterTableTimer = new PhaseTimer();
        logger.info(SchemaUpdateTool.generateFullWidthMessage("Executing " + getClass().getSimpleName() + " SchemaUpdate"));
        logger.info(ddl.executeStatement.get());
        MysqlTool.execute(connectionPool, ddl.executeStatement.get());
        alterTableTimer.add("Completed SchemaUpdate for " + tableName);
        logger.info(SchemaUpdateTool.generateFullWidthMessage(alterTableTimer.getElapsedString()));
    }
    if (ddl.printStatement.isEmpty()) {
        return Optional.empty();
    }
    SchemaUpdateTool.printSchemaUpdate(logger, ddl.printStatement.get());
    String errorMessage = null;
    if (ddl.preventStartUp) {
        errorMessage = "an alter on " + tableName + " is required";
    }
    return Optional.of(new SchemaUpdateResult(ddl.printStatement.get(), errorMessage, clientId));
}
Also used : MysqlCollation(io.datarouter.client.mysql.ddl.domain.MysqlCollation) PhaseTimer(io.datarouter.util.timer.PhaseTimer) SQLSyntaxErrorException(java.sql.SQLSyntaxErrorException) MysqlConnectionPool(io.datarouter.client.mysql.connection.MysqlConnectionPoolHolder.MysqlConnectionPool) Ddl(io.datarouter.client.mysql.ddl.generate.Ddl) SqlAlterTableGenerator(io.datarouter.client.mysql.ddl.generate.SqlAlterTableGeneratorFactory.SqlAlterTableGenerator) Field(io.datarouter.model.field.Field) MysqlCharacterSet(io.datarouter.client.mysql.ddl.domain.MysqlCharacterSet) MysqlRowFormat(io.datarouter.client.mysql.ddl.domain.MysqlRowFormat) MysqlTableOptions(io.datarouter.client.mysql.ddl.domain.MysqlTableOptions) ManagedNode(io.datarouter.storage.node.type.index.ManagedNode) List(java.util.List) SqlTable(io.datarouter.client.mysql.ddl.domain.SqlTable) SchemaUpdateResult(io.datarouter.storage.config.schema.SchemaUpdateResult) IndexedStorage(io.datarouter.storage.node.op.raw.IndexedStorage)

Example 3 with SchemaUpdateResult

use of io.datarouter.storage.config.schema.SchemaUpdateResult in project datarouter by hotpads.

the class SpannerSingleTableSchemaUpdateService method performSchemaUpdate.

public Optional<SchemaUpdateResult> performSchemaUpdate(ClientId clientId, Supplier<List<String>> existingTableNames, PhysicalNode<?, ?, ?> physicalNode) {
    String tableName = physicalNode.getFieldInfo().getTableName();
    List<Field<?>> primaryKeyFields = physicalNode.getFieldInfo().getSamplePrimaryKey().getFields();
    List<? extends SpannerBaseFieldCodec<?, ?>> primaryKeyCodecs = fieldCodecRegistry.createCodecs(primaryKeyFields);
    for (SpannerBaseFieldCodec<?, ?> codec : primaryKeyCodecs) {
        if (codec.getSpannerColumnType().isArray()) {
            throw new RuntimeException("Invalid field type used for primary key: " + codec.getField().getKey().getName());
        }
    }
    List<SpannerIndex> indexes = new ArrayList<>();
    List<SpannerIndex> uniqueIndexes = Scanner.of(physicalNode.getFieldInfo().getUniqueIndexes().entrySet()).map(entry -> new SpannerIndex(tableName, entry.getKey(), entry.getValue(), Collections.emptyList(), true)).list();
    var statements = new SpannerUpdateStatements();
    String entityTableName = null;
    if (physicalNode instanceof IndexedStorage) {
        IndexedStorage<?, ?> indexedStorage = (IndexedStorage<?, ?>) physicalNode;
        indexes = Scanner.of(indexedStorage.getManagedNodes()).map(node -> new SpannerIndex(tableName, node.getName(), node.getIndexEntryFieldInfo().getPrimaryKeyFields(), node.getIndexEntryFieldInfo().getFields(), false)).list();
    }
    List<SpannerColumn> primaryKeyColumns = Scanner.of(primaryKeyCodecs).map(codec -> codec.getSpannerColumn(false)).list();
    List<SpannerColumn> nonKeyColumns = Scanner.of(fieldCodecRegistry.createCodecs(physicalNode.getFieldInfo().getNonKeyFields())).map(codec -> codec.getSpannerColumn(true)).list();
    if (!existingTableNames.get().contains(tableName)) {
        statements.updateFunction(tableOperationsGenerator.createTable(tableName, primaryKeyColumns, nonKeyColumns, entityTableName), updateOptions::getCreateTables, true);
        Scanner.of(indexes, uniqueIndexes).concat(Scanner::of).map(index -> createIndex(index, primaryKeyColumns)).forEach(statement -> statements.updateFunction(statement, updateOptions::getCreateTables, true));
    } else {
        DatabaseClient databaseClient = clientsHolder.getDatabaseClient(clientId);
        List<SpannerColumn> allColumns = Scanner.of(primaryKeyColumns, nonKeyColumns).concat(Scanner::of).list();
        ResultSet columnRs = databaseClient.singleUse().executeQuery(Statement.of(tableOperationsGenerator.getTableSchema(tableName)));
        ResultSet primaryKeyRs = databaseClient.singleUse().executeQuery(Statement.of(tableOperationsGenerator.getTableIndexColumnsSchema(tableName, "PRIMARY_KEY")));
        tableAlterSchemaService.generateUpdateStatementColumns(tableName, allColumns, primaryKeyColumns, columnRs, primaryKeyRs, statements);
        ResultSet indexesRs = databaseClient.singleUse().executeQuery(Statement.of(tableOperationsGenerator.getTableIndexSchema(tableName)));
        Set<String> currentIndexes = tableAlterSchemaService.getIndexes(indexesRs);
        Scanner.of(indexes, uniqueIndexes).concat(Scanner::of).forEach(index -> {
            Statement tableIndexColumnsSchema = Statement.of(tableOperationsGenerator.getTableIndexColumnsSchema(tableName, index.getIndexName()));
            ResultSet indexRs = databaseClient.singleUse().executeQuery(tableIndexColumnsSchema);
            if (!tableAlterSchemaService.indexEqual(index, indexRs)) {
                if (currentIndexes.contains(index.getIndexName())) {
                    statements.updateFunction(tableOperationsGenerator.dropIndex(index.getIndexName()), updateOptions::getDropIndexes, false);
                }
                statements.updateFunction(createIndex(index, primaryKeyColumns), updateOptions::getAddIndexes, true);
            }
            currentIndexes.remove(index.getIndexName());
        });
        currentIndexes.forEach(name -> statements.updateFunction(tableOperationsGenerator.dropIndex(name), updateOptions::getDropIndexes, false));
    }
    String errorMessage = null;
    if (!statements.getExecuteStatements().isEmpty()) {
        logger.info(SchemaUpdateTool.generateFullWidthMessage("Executing Spanner " + getClass().getSimpleName() + " SchemaUpdate"));
        logger.info(String.join("\n\n", statements.getExecuteStatements()));
        Database database = clientsHolder.getDatabase(clientId);
        OperationFuture<Void, UpdateDatabaseDdlMetadata> future = database.updateDdl(statements.getExecuteStatements(), null);
        errorMessage = FutureTool.get(future.getPollingFuture().getAttemptResult()).getErrorMessage();
        if (StringTool.notNullNorEmptyNorWhitespace(errorMessage)) {
            logger.error(errorMessage);
        }
    }
    if (statements.getPreventStartUp()) {
        errorMessage = "an alter on Spanner table " + tableName + " is required";
    }
    if (statements.getPrintStatements().isEmpty()) {
        return Optional.empty();
    }
    String printStatement = statements.getPrintStatements().stream().map(statement -> statement + ";").collect(Collectors.joining("\n"));
    SchemaUpdateTool.printSchemaUpdate(logger, printStatement);
    return Optional.of(new SchemaUpdateResult(printStatement, errorMessage, clientId));
}
Also used : Scanner(io.datarouter.scanner.Scanner) SchemaUpdateTool(io.datarouter.storage.config.schema.SchemaUpdateTool) OperationFuture(com.google.api.gax.longrunning.OperationFuture) SpannerDatabaseClientsHolder(io.datarouter.gcp.spanner.connection.SpannerDatabaseClientsHolder) LoggerFactory(org.slf4j.LoggerFactory) PhysicalNode(io.datarouter.storage.node.type.physical.PhysicalNode) DatabaseClient(com.google.cloud.spanner.DatabaseClient) Singleton(javax.inject.Singleton) Supplier(java.util.function.Supplier) ArrayList(java.util.ArrayList) HashSet(java.util.HashSet) Inject(javax.inject.Inject) ResultSet(com.google.cloud.spanner.ResultSet) ClientId(io.datarouter.storage.client.ClientId) Logger(org.slf4j.Logger) Database(com.google.cloud.spanner.Database) Set(java.util.Set) StringTool(io.datarouter.util.string.StringTool) Collectors(java.util.stream.Collectors) SchemaUpdateOptions(io.datarouter.storage.config.schema.SchemaUpdateOptions) UpdateDatabaseDdlMetadata(com.google.spanner.admin.database.v1.UpdateDatabaseDdlMetadata) Statement(com.google.cloud.spanner.Statement) List(java.util.List) Field(io.datarouter.model.field.Field) SpannerFieldCodecRegistry(io.datarouter.gcp.spanner.field.SpannerFieldCodecRegistry) Optional(java.util.Optional) IndexedStorage(io.datarouter.storage.node.op.raw.IndexedStorage) Collections(java.util.Collections) SpannerBaseFieldCodec(io.datarouter.gcp.spanner.field.SpannerBaseFieldCodec) SchemaUpdateResult(io.datarouter.storage.config.schema.SchemaUpdateResult) FutureTool(io.datarouter.util.concurrent.FutureTool) Scanner(io.datarouter.scanner.Scanner) ArrayList(java.util.ArrayList) Field(io.datarouter.model.field.Field) UpdateDatabaseDdlMetadata(com.google.spanner.admin.database.v1.UpdateDatabaseDdlMetadata) ResultSet(com.google.cloud.spanner.ResultSet) Database(com.google.cloud.spanner.Database) IndexedStorage(io.datarouter.storage.node.op.raw.IndexedStorage) Statement(com.google.cloud.spanner.Statement) DatabaseClient(com.google.cloud.spanner.DatabaseClient) SchemaUpdateResult(io.datarouter.storage.config.schema.SchemaUpdateResult)

Aggregations

SchemaUpdateResult (io.datarouter.storage.config.schema.SchemaUpdateResult)3 Field (io.datarouter.model.field.Field)2 IndexedStorage (io.datarouter.storage.node.op.raw.IndexedStorage)2 ArrayList (java.util.ArrayList)2 List (java.util.List)2 OperationFuture (com.google.api.gax.longrunning.OperationFuture)1 Database (com.google.cloud.spanner.Database)1 DatabaseClient (com.google.cloud.spanner.DatabaseClient)1 ResultSet (com.google.cloud.spanner.ResultSet)1 Statement (com.google.cloud.spanner.Statement)1 UpdateDatabaseDdlMetadata (com.google.spanner.admin.database.v1.UpdateDatabaseDdlMetadata)1 MysqlConnectionPool (io.datarouter.client.mysql.connection.MysqlConnectionPoolHolder.MysqlConnectionPool)1 MysqlCharacterSet (io.datarouter.client.mysql.ddl.domain.MysqlCharacterSet)1 MysqlCollation (io.datarouter.client.mysql.ddl.domain.MysqlCollation)1 MysqlRowFormat (io.datarouter.client.mysql.ddl.domain.MysqlRowFormat)1 MysqlTableOptions (io.datarouter.client.mysql.ddl.domain.MysqlTableOptions)1 SqlTable (io.datarouter.client.mysql.ddl.domain.SqlTable)1 Ddl (io.datarouter.client.mysql.ddl.generate.Ddl)1 SqlAlterTableGenerator (io.datarouter.client.mysql.ddl.generate.SqlAlterTableGeneratorFactory.SqlAlterTableGenerator)1 SpannerDatabaseClientsHolder (io.datarouter.gcp.spanner.connection.SpannerDatabaseClientsHolder)1