Line data Source code
1 : // Copyright (C) 2017 The Android Open Source Project 2 : // 3 : // Licensed under the Apache License, Version 2.0 (the "License"); 4 : // you may not use this file except in compliance with the License. 5 : // You may obtain a copy of the License at 6 : // 7 : // http://www.apache.org/licenses/LICENSE-2.0 8 : // 9 : // Unless required by applicable law or agreed to in writing, software 10 : // distributed under the License is distributed on an "AS IS" BASIS, 11 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 : // See the License for the specific language governing permissions and 13 : // limitations under the License. 14 : 15 : package com.google.gerrit.pgm; 16 : 17 : import com.google.auto.value.AutoValue; 18 : import com.google.common.base.Stopwatch; 19 : import com.google.common.base.Strings; 20 : import com.google.gerrit.pgm.util.SiteProgram; 21 : import com.google.gerrit.server.config.GerritServerConfig; 22 : import com.google.gerrit.server.config.SitePaths; 23 : import com.google.gerrit.server.config.ThreadSettingsConfig; 24 : import com.google.gerrit.server.schema.JdbcAccountPatchReviewStore; 25 : import com.google.inject.Injector; 26 : import com.google.inject.Key; 27 : import java.sql.Connection; 28 : import java.sql.PreparedStatement; 29 : import java.sql.ResultSet; 30 : import java.sql.SQLException; 31 : import java.sql.Statement; 32 : import java.util.ArrayList; 33 : import java.util.List; 34 : import java.util.concurrent.TimeUnit; 35 : import org.eclipse.jgit.lib.Config; 36 : import org.kohsuke.args4j.Option; 37 : 38 : /** Migrates AccountPatchReviewDb from one to another */ 39 0 : public class MigrateAccountPatchReviewDb extends SiteProgram { 40 : 41 : @Option(name = "--sourceUrl", usage = "Url of source database") 42 : private String sourceUrl; 43 : 44 : @Option( 45 : name = "--chunkSize", 46 : usage = "chunk size of fetching from source and push to target on each time") 47 0 : private static long chunkSize = 100000; 48 : 49 : @Override 50 : public int run() throws Exception { 51 0 : Injector dbInjector = createDbInjector(); 52 0 : SitePaths sitePaths = new SitePaths(getSitePath()); 53 0 : ThreadSettingsConfig threadSettingsConfig = dbInjector.getInstance(ThreadSettingsConfig.class); 54 0 : Config fakeCfg = new Config(); 55 0 : if (!Strings.isNullOrEmpty(sourceUrl)) { 56 0 : fakeCfg.setString("accountPatchReviewDb", null, "url", sourceUrl); 57 : } 58 0 : JdbcAccountPatchReviewStore sourceJdbcAccountPatchReviewStore = 59 0 : JdbcAccountPatchReviewStore.createAccountPatchReviewStore( 60 : fakeCfg, sitePaths, threadSettingsConfig); 61 : 62 0 : Config cfg = dbInjector.getInstance(Key.get(Config.class, GerritServerConfig.class)); 63 0 : String targetUrl = cfg.getString("accountPatchReviewDb", null, "url"); 64 0 : if (targetUrl == null) { 65 0 : System.err.println("accountPatchReviewDb.url is null in gerrit.config"); 66 0 : return 1; 67 : } 68 0 : System.out.println("target Url: " + targetUrl); 69 0 : JdbcAccountPatchReviewStore targetJdbcAccountPatchReviewStore = 70 0 : JdbcAccountPatchReviewStore.createAccountPatchReviewStore( 71 : cfg, sitePaths, threadSettingsConfig); 72 0 : targetJdbcAccountPatchReviewStore.createTableIfNotExists(); 73 : 74 0 : if (!isTargetTableEmpty(targetJdbcAccountPatchReviewStore)) { 75 0 : System.err.println("target table is not empty, cannot proceed"); 76 0 : return 1; 77 : } 78 : 79 0 : try (Connection sourceCon = sourceJdbcAccountPatchReviewStore.getConnection(); 80 0 : Connection targetCon = targetJdbcAccountPatchReviewStore.getConnection(); 81 0 : PreparedStatement sourceStmt = 82 0 : sourceCon.prepareStatement( 83 : "SELECT account_id, change_id, patch_set_id, file_name " 84 : + "FROM account_patch_reviews " 85 : + "LIMIT ? " 86 : + "OFFSET ?"); 87 0 : PreparedStatement targetStmt = 88 0 : targetCon.prepareStatement( 89 : "INSERT INTO account_patch_reviews " 90 : + "(account_id, change_id, patch_set_id, file_name) VALUES " 91 : + "(?, ?, ?, ?)")) { 92 0 : targetCon.setAutoCommit(false); 93 0 : long offset = 0; 94 0 : Stopwatch sw = Stopwatch.createStarted(); 95 0 : List<Row> rows = selectRows(sourceStmt, offset); 96 0 : while (!rows.isEmpty()) { 97 0 : insertRows(targetCon, targetStmt, rows); 98 0 : offset += rows.size(); 99 0 : System.out.printf("%8d rows migrated\n", offset); 100 0 : rows = selectRows(sourceStmt, offset); 101 : } 102 0 : double t = sw.elapsed(TimeUnit.MILLISECONDS) / 1000d; 103 0 : System.out.printf("Migrated %d rows in %.01fs (%.01f/s)\n", offset, t, offset / t); 104 : } 105 0 : return 0; 106 : } 107 : 108 : @AutoValue 109 0 : abstract static class Row { 110 : abstract int accountId(); 111 : 112 : abstract int changeId(); 113 : 114 : abstract int patchSetId(); 115 : 116 : abstract String fileName(); 117 : } 118 : 119 : private static boolean isTargetTableEmpty(JdbcAccountPatchReviewStore store) throws SQLException { 120 0 : try (Connection con = store.getConnection(); 121 0 : Statement s = con.createStatement(); 122 0 : ResultSet r = s.executeQuery("SELECT COUNT(1) FROM account_patch_reviews")) { 123 0 : if (r.next()) { 124 0 : return r.getInt(1) == 0; 125 : } 126 0 : return true; 127 0 : } 128 : } 129 : 130 : private static List<Row> selectRows(PreparedStatement stmt, long offset) throws SQLException { 131 0 : List<Row> results = new ArrayList<>(); 132 0 : stmt.setLong(1, chunkSize); 133 0 : stmt.setLong(2, offset); 134 0 : try (ResultSet rs = stmt.executeQuery()) { 135 0 : while (rs.next()) { 136 0 : results.add( 137 : new AutoValue_MigrateAccountPatchReviewDb_Row( 138 0 : rs.getInt("account_id"), 139 0 : rs.getInt("change_id"), 140 0 : rs.getInt("patch_set_id"), 141 0 : rs.getString("file_name"))); 142 : } 143 : } 144 0 : return results; 145 : } 146 : 147 : private static void insertRows(Connection con, PreparedStatement stmt, List<Row> rows) 148 : throws SQLException { 149 0 : for (Row r : rows) { 150 0 : stmt.setLong(1, r.accountId()); 151 0 : stmt.setLong(2, r.changeId()); 152 0 : stmt.setLong(3, r.patchSetId()); 153 0 : stmt.setString(4, r.fileName()); 154 0 : stmt.addBatch(); 155 0 : } 156 0 : stmt.executeBatch(); 157 0 : con.commit(); 158 0 : } 159 : }