Skip to content

Commit dd3bcae

Browse files
committed
Refactor ProxyOnlineVoteCacheTable to improve migration handling
1 parent 499e4c9 commit dd3bcae

File tree

1 file changed

+54
-30
lines changed

1 file changed

+54
-30
lines changed

VotingPlugin/src/main/java/com/bencodez/votingplugin/proxy/cache/ProxyOnlineVoteCacheTable.java

Lines changed: 54 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,12 @@
44
import java.sql.PreparedStatement;
55
import java.sql.ResultSet;
66
import java.sql.SQLException;
7+
import java.sql.Statement; // NEW
78
import java.util.ArrayList;
89
import java.util.List;
10+
import java.util.Set; // NEW
911
import java.util.UUID;
12+
import java.util.concurrent.ConcurrentHashMap; // NEW
1013

1114
import com.bencodez.simpleapi.sql.mysql.AbstractSqlTable;
1215
import com.bencodez.simpleapi.sql.mysql.DbType;
@@ -17,6 +20,10 @@
1720

1821
public abstract class ProxyOnlineVoteCacheTable extends AbstractSqlTable {
1922

23+
// Prevent repeated startup DDL across multiple instances/subclasses
24+
private static final Set<String> MIGRATED_VOTEID = ConcurrentHashMap.newKeySet();
25+
private static final Set<String> ENSURED_INDEXES = ConcurrentHashMap.newKeySet();
26+
2027
@Override
2128
public String getPrimaryKeyColumn() {
2229
return "id";
@@ -67,9 +74,18 @@ public void removeVotesByUuid(String uuid) {
6774
public ProxyOnlineVoteCacheTable(MySQL existingMysql, String tablePrefix, boolean debug) {
6875
super((tablePrefix != null ? tablePrefix : "") + "votingplugin_onlinevotecache", existingMysql, debug);
6976

77+
// best-effort migrations (no nested connections)
7078
alterColumnType("uuid", bestUuidType());
71-
addVoteIdColumnIfMissing();
72-
ensureIndexes();
79+
addVoteIdColumnIfMissingOnce();
80+
ensureIndexesOnce();
81+
}
82+
83+
public ProxyOnlineVoteCacheTable(MysqlConfig config, boolean debug) {
84+
super("votingplugin_onlinevotecache", config, debug);
85+
86+
alterColumnType("uuid", bestUuidType());
87+
addVoteIdColumnIfMissingOnce();
88+
ensureIndexesOnce();
7389
}
7490

7591
public void updateVoteText(OfflineBungeeVote vote, String newText) {
@@ -100,40 +116,46 @@ public void updateVoteText(OfflineBungeeVote vote, String newText) {
100116
}
101117
}
102118

103-
public ProxyOnlineVoteCacheTable(MysqlConfig config, boolean debug) {
104-
super("votingplugin_onlinevotecache", config, debug);
119+
private void ensureIndexesOnce() {
120+
if (getDbType() != DbType.POSTGRESQL) {
121+
return;
122+
}
105123

106-
alterColumnType("uuid", bestUuidType());
107-
addVoteIdColumnIfMissing();
108-
ensureIndexes();
124+
String key = getDbType() + ":" + getTableName() + ":indexes";
125+
if (!ENSURED_INDEXES.add(key)) {
126+
return;
127+
}
128+
129+
// Use ONE connection for all index DDL to avoid pool starvation (pool=1 safe)
130+
try (Connection conn = mysql.getConnectionManager().getConnection(); Statement st = conn.createStatement()) {
131+
132+
st.executeUpdate("CREATE INDEX IF NOT EXISTS idx_uuid ON " + qi(getTableName()) + " (" + qi("uuid") + ");");
133+
st.executeUpdate("CREATE INDEX IF NOT EXISTS idx_time ON " + qi(getTableName()) + " (" + qi("time") + ");");
134+
135+
} catch (SQLException e) {
136+
debug(e);
137+
}
109138
}
110139

111-
private void ensureIndexes() {
112-
if (getDbType() == DbType.POSTGRESQL) {
113-
try {
114-
new Query(mysql,
115-
"CREATE INDEX IF NOT EXISTS idx_uuid ON " + qi(getTableName()) + " (" + qi("uuid") + ");")
116-
.executeUpdate();
117-
new Query(mysql,
118-
"CREATE INDEX IF NOT EXISTS idx_time ON " + qi(getTableName()) + " (" + qi("time") + ");")
119-
.executeUpdate();
120-
} catch (SQLException e) {
121-
debug(e);
122-
}
140+
private void addVoteIdColumnIfMissingOnce() {
141+
String key = getDbType() + ":" + getTableName() + ":voteid";
142+
if (!MIGRATED_VOTEID.add(key)) {
143+
return;
123144
}
145+
addVoteIdColumnIfMissing();
124146
}
125147

126148
private void addVoteIdColumnIfMissing() {
127-
String schemaFilter;
128-
if (getDbType() == DbType.POSTGRESQL) {
129-
schemaFilter = "table_schema = current_schema()";
130-
} else {
131-
schemaFilter = "TABLE_SCHEMA = DATABASE()";
132-
}
149+
final boolean pg = getDbType() == DbType.POSTGRESQL;
150+
151+
final String schemaFilter = pg ? "table_schema = current_schema()" : "TABLE_SCHEMA = DATABASE()";
133152

134-
String checkSql = "SELECT 1 FROM information_schema.columns WHERE " + schemaFilter
135-
+ " AND table_name = ? AND column_name = ? LIMIT 1;";
153+
// Lowercase compare avoids casing issues on some setups
154+
final String checkSql = "SELECT 1 FROM information_schema.columns WHERE " + schemaFilter
155+
+ " AND LOWER(table_name) = LOWER(?) AND LOWER(column_name) = LOWER(?) LIMIT 1;";
136156

157+
// IMPORTANT: do check + alter on SAME connection to avoid deadlock when
158+
// poolSize=1
137159
try (Connection conn = mysql.getConnectionManager().getConnection();
138160
PreparedStatement ps = conn.prepareStatement(checkSql)) {
139161

@@ -142,12 +164,14 @@ private void addVoteIdColumnIfMissing() {
142164

143165
try (ResultSet rs = ps.executeQuery()) {
144166
if (rs.next()) {
145-
return;
167+
return; // exists
146168
}
147169
}
148170

149171
String alter = "ALTER TABLE " + qi(getTableName()) + " ADD COLUMN " + qi("voteid") + " VARCHAR(36);";
150-
new Query(mysql, alter).executeUpdate();
172+
try (Statement st = conn.createStatement()) {
173+
st.executeUpdate(alter);
174+
}
151175

152176
} catch (SQLException e) {
153177
logSevere("Failed to add voteid column to " + getTableName() + ": " + e.getMessage());
@@ -162,7 +186,7 @@ public void insertVote(UUID voteId, String uuid, String playerName, String servi
162186

163187
String sql = "INSERT INTO " + qi(getTableName()) + " (" + qi("uuid") + ", " + qi("voteid") + ", "
164188
+ qi("playerName") + ", " + qi("service") + ", " + qi("time") + ", " + qi("realVote") + ", "
165-
+ qi("text") + ") " + "VALUES (?, ?, ?, ?, ?, ?, ?);";
189+
+ qi("text") + ") VALUES (?, ?, ?, ?, ?, ?, ?);";
166190

167191
try (Connection conn = mysql.getConnectionManager().getConnection();
168192
PreparedStatement ps = conn.prepareStatement(sql)) {

0 commit comments

Comments
 (0)