/* * Licensed to Crate under one or more contributor license agreements. * See the NOTICE file distributed with this work for additional * information regarding copyright ownership. Crate licenses this file * to you 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. * * However, if you have executed another commercial license agreement * with Crate these terms will supersede the license and you may use the * software solely pursuant to the terms of the relevant commercial * agreement. */ package io.crate.operation.reference.sys.check.cluster; import io.crate.action.sql.ResultReceiver; import io.crate.action.sql.SQLOperations; import io.crate.data.Row; import io.crate.operation.reference.sys.check.AbstractSysCheck; import org.apache.logging.log4j.Logger; import org.apache.lucene.util.BytesRef; import org.apache.lucene.util.Version; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.inject.Singleton; import org.elasticsearch.common.logging.Loggers; import org.elasticsearch.common.settings.Settings; import javax.annotation.Nonnull; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.concurrent.CompletableFuture; @Singleton public class TablesNeedUpgradeSysCheck extends AbstractSysCheck { public static final int ID = 3; public static final String DESCRIPTION = "The following tables need to be upgraded for compatibility with future versions of CrateDB: "; private static final String STMT = "select schema_name || '.' || table_name, min_lucene_version " + "from sys.shards where min_lucene_version not like '" + Version.LATEST.major+ ".%.%' " + "order by 1"; private static final int LIMIT = 50_000; private static final String PREP_STMT_NAME = "tables_need_upgrade_syscheck"; private final Logger logger; private final SQLOperations.SQLDirectExecutor sqlDirectExecutor; private volatile Collection<String> tablesNeedUpgrade; @Inject public TablesNeedUpgradeSysCheck(SQLOperations sqlOperations, Settings settings) { super(ID, DESCRIPTION, Severity.MEDIUM); this.sqlDirectExecutor = sqlOperations.createSQLDirectExecutor( "sys", PREP_STMT_NAME, STMT, LIMIT); this.logger = Loggers.getLogger(TablesNeedUpgradeSysCheck.class, settings); } @Override public BytesRef description() { String linkedDescriptionBuilder = DESCRIPTION + tablesNeedUpgrade + ' ' + LINK_PATTERN + ID; return new BytesRef(linkedDescriptionBuilder); } @Override public CompletableFuture<?> computeResult() { final CompletableFuture<Collection<String>> result = new CompletableFuture<>(); try { sqlDirectExecutor.execute(new SycCheckResultReceiver(result), Collections.emptyList()); } catch (Throwable t) { result.completeExceptionally(t); } return result.whenComplete((tableNames, throwable) -> { if (throwable == null) { tablesNeedUpgrade = tableNames; } else { logger.error("error while checking for tables that need upgrade", throwable); } }); } @Override public boolean validate() { return tablesNeedUpgrade == null || tablesNeedUpgrade.isEmpty(); } private class SycCheckResultReceiver implements ResultReceiver { private final CompletableFuture<Collection<String>> result; private final Collection<String> tables; private SycCheckResultReceiver(CompletableFuture<Collection<String>> result) { this.result = result; this.tables = new HashSet<>(); } @Override public void setNextRow(Row row) { // Row[0] = table_name, Row[1] = min_lucene_version if (LuceneVersionChecks.isUpgradeRequired(((BytesRef) row.get(1)).utf8ToString())) { tables.add(((BytesRef) row.get(0)).utf8ToString()); } } @Override public void batchFinished() { } @Override public void allFinished(boolean interrupted) { result.complete(tables); } @Override public void fail(@Nonnull Throwable t) { result.completeExceptionally(t); } @Override public CompletableFuture<Collection<String>> completionFuture() { return result; } } }