ApplyQualityGateMigration.java 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. import java.sql.*;
  2. import java.util.ArrayList;
  3. import java.util.List;
  4. /**
  5. * Apply lobster_execution_config quality gate columns on tenant DB(s).
  6. * Usage: java -cp mysql-connector-j.jar ApplyQualityGateMigration [tenantId...]
  7. * Default tenantId: 33
  8. */
  9. public class ApplyQualityGateMigration {
  10. private static final String MASTER_HOST = getenv("DB_HOST", "cq-cdb-8fjmemkb.sql.tencentcdb.com");
  11. private static final int MASTER_PORT = Integer.parseInt(getenv("DB_PORT", "27220"));
  12. private static final String MASTER_USER = getenv("DB_USER", "root");
  13. private static final String MASTER_PASSWORD = getenv("DB_PASSWORD", "Ylrz_1q2w3e4r5t6y");
  14. private static final String MASTER_DB = getenv("DB_NAME", "ylrz_saas");
  15. public static void main(String[] args) throws Exception {
  16. List<Long> tenantIds = new ArrayList<>();
  17. if (args.length == 0) {
  18. tenantIds.add(33L);
  19. } else {
  20. for (String arg : args) {
  21. tenantIds.add(Long.parseLong(arg.trim()));
  22. }
  23. }
  24. try (Connection master = open(MASTER_HOST, MASTER_PORT, MASTER_DB, MASTER_USER, MASTER_PASSWORD)) {
  25. for (Long tenantId : tenantIds) {
  26. TenantDb tenant = loadTenant(master, tenantId);
  27. if (tenant == null) {
  28. System.err.println("[SKIP] tenant " + tenantId + " not found");
  29. continue;
  30. }
  31. System.out.println("[RUN] tenant " + tenantId + " -> " + tenant.host + ":" + tenant.port + "/" + tenant.database);
  32. try (Connection tenantConn = open(tenant.host, tenant.port, tenant.database, tenant.user, tenant.password)) {
  33. applyQualityGateColumns(tenantConn);
  34. }
  35. System.out.println("[OK] tenant " + tenantId);
  36. }
  37. }
  38. }
  39. private static void applyQualityGateColumns(Connection conn) throws SQLException {
  40. addColumnIfMissing(conn, "lobster_execution_config", "quality_gate_enabled",
  41. "ALTER TABLE lobster_execution_config ADD COLUMN quality_gate_enabled tinyint NOT NULL DEFAULT 1 "
  42. + "COMMENT 'quality gate enabled' AFTER auto_start_instance_on_tag");
  43. addColumnIfMissing(conn, "lobster_execution_config", "quality_pass_score_percent",
  44. "ALTER TABLE lobster_execution_config ADD COLUMN quality_pass_score_percent int NOT NULL DEFAULT 75 "
  45. + "COMMENT 'pass score percent' AFTER quality_gate_enabled");
  46. addColumnIfMissing(conn, "lobster_execution_config", "quality_target_score_percent",
  47. "ALTER TABLE lobster_execution_config ADD COLUMN quality_target_score_percent int NOT NULL DEFAULT 75 "
  48. + "COMMENT 'target score percent' AFTER quality_pass_score_percent");
  49. addColumnIfMissing(conn, "lobster_execution_config", "quality_min_node_score_percent",
  50. "ALTER TABLE lobster_execution_config ADD COLUMN quality_min_node_score_percent int NOT NULL DEFAULT 60 "
  51. + "COMMENT 'min node score percent' AFTER quality_target_score_percent");
  52. addColumnIfMissing(conn, "lobster_execution_config", "quality_min_e2e_avg_score_percent",
  53. "ALTER TABLE lobster_execution_config ADD COLUMN quality_min_e2e_avg_score_percent int NOT NULL DEFAULT 75 "
  54. + "COMMENT 'min e2e avg percent' AFTER quality_min_node_score_percent");
  55. addColumnIfMissing(conn, "lobster_execution_config", "quality_multiturn_min_avg_score_percent",
  56. "ALTER TABLE lobster_execution_config ADD COLUMN quality_multiturn_min_avg_score_percent int NOT NULL DEFAULT 45 "
  57. + "COMMENT 'multiturn min avg percent' AFTER quality_min_e2e_avg_score_percent");
  58. }
  59. private static void addColumnIfMissing(Connection conn, String table, String column, String ddl) throws SQLException {
  60. if (columnExists(conn, table, column)) {
  61. System.out.println(" column exists: " + table + "." + column);
  62. return;
  63. }
  64. try (Statement st = conn.createStatement()) {
  65. st.execute(ddl);
  66. System.out.println(" added column: " + table + "." + column);
  67. }
  68. }
  69. private static boolean columnExists(Connection conn, String table, String column) throws SQLException {
  70. String sql = "SELECT COUNT(*) FROM information_schema.COLUMNS "
  71. + "WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ? AND COLUMN_NAME = ?";
  72. try (PreparedStatement ps = conn.prepareStatement(sql)) {
  73. ps.setString(1, table);
  74. ps.setString(2, column);
  75. try (ResultSet rs = ps.executeQuery()) {
  76. return rs.next() && rs.getInt(1) > 0;
  77. }
  78. }
  79. }
  80. private static TenantDb loadTenant(Connection master, long tenantId) throws SQLException {
  81. String sql = "SELECT db_url, db_account, db_pwd FROM tenant_info WHERE id = ? AND status = 1 LIMIT 1";
  82. try (PreparedStatement ps = master.prepareStatement(sql)) {
  83. ps.setLong(1, tenantId);
  84. try (ResultSet rs = ps.executeQuery()) {
  85. if (!rs.next()) {
  86. return null;
  87. }
  88. String dbUrl = rs.getString("db_url");
  89. String user = rs.getString("db_account");
  90. String password = rs.getString("db_pwd");
  91. return parseJdbcUrl(dbUrl, user, password);
  92. }
  93. }
  94. }
  95. private static TenantDb parseJdbcUrl(String jdbcUrl, String user, String password) {
  96. if (jdbcUrl == null || !jdbcUrl.startsWith("jdbc:mysql://")) {
  97. throw new IllegalArgumentException("Unsupported db_url: " + jdbcUrl);
  98. }
  99. String body = jdbcUrl.substring("jdbc:mysql://".length());
  100. int slash = body.indexOf('/');
  101. if (slash < 0) {
  102. throw new IllegalArgumentException("Invalid db_url: " + jdbcUrl);
  103. }
  104. String hostPort = body.substring(0, slash);
  105. String rest = body.substring(slash + 1);
  106. int q = rest.indexOf('?');
  107. String database = q >= 0 ? rest.substring(0, q) : rest;
  108. String host;
  109. int port;
  110. int colon = hostPort.indexOf(':');
  111. if (colon >= 0) {
  112. host = hostPort.substring(0, colon);
  113. port = Integer.parseInt(hostPort.substring(colon + 1));
  114. } else {
  115. host = hostPort;
  116. port = 3306;
  117. }
  118. TenantDb t = new TenantDb();
  119. t.host = host;
  120. t.port = port;
  121. t.database = database;
  122. t.user = user != null ? user : MASTER_USER;
  123. t.password = password != null ? password : MASTER_PASSWORD;
  124. return t;
  125. }
  126. private static Connection open(String host, int port, String database, String user, String password)
  127. throws SQLException {
  128. String url = "jdbc:mysql://" + host + ":" + port + "/" + database
  129. + "?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&allowMultiQueries=true";
  130. return DriverManager.getConnection(url, user, password);
  131. }
  132. private static String getenv(String key, String defaultValue) {
  133. String v = System.getenv(key);
  134. return v != null && !v.isEmpty() ? v : defaultValue;
  135. }
  136. private static final class TenantDb {
  137. String host;
  138. int port;
  139. String database;
  140. String user;
  141. String password;
  142. }
  143. }