Skip to content

Commit f4b82bb

Browse files
committed
Refactor forEachUser method to implement pagination
1 parent 4a1d29a commit f4b82bb

File tree

1 file changed

+85
-82
lines changed
  • AdvancedCore/src/main/java/com/bencodez/advancedcore/api/user/userstorage/mysql

1 file changed

+85
-82
lines changed

AdvancedCore/src/main/java/com/bencodez/advancedcore/api/user/userstorage/mysql/MySQL.java

Lines changed: 85 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@
1111
import java.util.Set;
1212
import java.util.UUID;
1313
import java.util.concurrent.ConcurrentHashMap;
14-
import java.util.function.BiConsumer;
15-
import java.util.function.Consumer;
1614

1715
import org.bukkit.configuration.ConfigurationSection;
1816

@@ -185,110 +183,115 @@ public boolean containsUUID(String uuid) {
185183
* @param onFinished called once at the end with the number of rows processed
186184
* (even if 0)
187185
*/
188-
public void forEachUser(BiConsumer<UUID, ArrayList<Column>> perUser, Consumer<Integer> onFinished) {
186+
public void forEachUser(java.util.function.BiConsumer<UUID, ArrayList<Column>> perUser,
187+
java.util.function.Consumer<Integer> onFinished) {
188+
189189
int processed = 0;
190+
final int pageSize = 500; // tune 250/500/1000
190191

191-
// NOTE: Selecting * means we still read every column, but we DON'T store all
192-
// rows at once.
193-
final String sqlStr = "SELECT * FROM " + qi(tableName) + ";";
194-
plugin.devDebug("DB QUERY: " + sqlStr);
192+
String lastUuid = ""; // start lower than any real uuid string
195193

196-
try (Connection conn = mysql.getConnectionManager().getConnection();
197-
PreparedStatement ps = conn.prepareStatement(sqlStr, ResultSet.TYPE_FORWARD_ONLY,
198-
ResultSet.CONCUR_READ_ONLY)) {
194+
while (true) {
195+
int rowsThisPage = 0;
199196

200-
// Streaming hints:
201-
// - MySQL: Integer.MIN_VALUE triggers row-by-row streaming for some drivers
202-
// - Postgres: a positive fetch size enables cursor-based fetching
203-
try {
204-
if (dbType == DbType.POSTGRESQL) {
205-
conn.setAutoCommit(false); // required for cursor fetch in pg
206-
ps.setFetchSize(500);
207-
} else {
208-
ps.setFetchSize(Integer.MIN_VALUE);
209-
}
210-
} catch (Exception ignore) {
211-
// Driver may not support the hint; safe to ignore.
212-
}
197+
String sqlStr = "SELECT * FROM " + qi(tableName) + " WHERE " + qi("uuid") + " > ?" + " ORDER BY "
198+
+ qi("uuid") + " ASC" + " LIMIT " + pageSize + ";";
199+
200+
try (Connection conn = mysql.getConnectionManager().getConnection();
201+
PreparedStatement ps = conn.prepareStatement(sqlStr, ResultSet.TYPE_FORWARD_ONLY,
202+
ResultSet.CONCUR_READ_ONLY)) {
203+
204+
ps.setString(1, lastUuid);
213205

214-
try (ResultSet rs = ps.executeQuery()) {
215-
final int colCount = rs.getMetaData().getColumnCount();
216-
217-
while (rs.next()) {
218-
UUID uuid = null;
219-
ArrayList<Column> cols = new ArrayList<>(colCount);
220-
221-
for (int i = 1; i <= colCount; i++) {
222-
String columnName = rs.getMetaData().getColumnLabel(i);
223-
Column rCol;
224-
225-
if (plugin.getUserManager().getDataManager().isInt(columnName)) {
226-
rCol = new Column(columnName, DataType.INTEGER);
227-
try {
228-
rCol.setValue(new DataValueInt(rs.getInt(i)));
229-
} catch (Exception e) {
230-
String data = rs.getString(i);
231-
if (data != null) {
232-
try {
233-
rCol.setValue(new DataValueInt(Integer.parseInt(data)));
234-
} catch (NumberFormatException ex) {
206+
try (ResultSet rs = ps.executeQuery()) {
207+
final int colCount = rs.getMetaData().getColumnCount();
208+
209+
while (rs.next()) {
210+
ArrayList<Column> cols = new ArrayList<>(colCount);
211+
UUID uuid = null;
212+
String uuidStrForSeek = null;
213+
214+
for (int i = 1; i <= colCount; i++) {
215+
String columnName = rs.getMetaData().getColumnLabel(i);
216+
Column rCol;
217+
218+
if (plugin.getUserManager().getDataManager().isInt(columnName)) {
219+
rCol = new Column(columnName, DataType.INTEGER);
220+
try {
221+
rCol.setValue(new DataValueInt(rs.getInt(i)));
222+
} catch (Exception e) {
223+
String data = rs.getString(i);
224+
if (data != null) {
225+
try {
226+
rCol.setValue(new DataValueInt(Integer.parseInt(data)));
227+
} catch (NumberFormatException ex) {
228+
rCol.setValue(new DataValueInt(0));
229+
}
230+
} else {
235231
rCol.setValue(new DataValueInt(0));
236232
}
237-
} else {
238-
rCol.setValue(new DataValueInt(0));
239233
}
240-
}
241-
} else if (plugin.getUserManager().getDataManager().isBoolean(columnName)) {
242-
rCol = new Column(columnName, DataType.BOOLEAN);
243-
rCol.setValue(new DataValueBoolean(Boolean.valueOf(rs.getString(i))));
244-
} else {
245-
rCol = new Column(columnName, DataType.STRING);
246-
247-
if ("uuid".equalsIgnoreCase(columnName)) {
248-
// Extract UUID efficiently and correctly for pg uuid type
249-
if (dbType == DbType.POSTGRESQL) {
250-
Object obj = rs.getObject(i);
251-
if (obj instanceof java.util.UUID) {
252-
uuid = (java.util.UUID) obj;
253-
rCol.setValue(new DataValueString(uuid.toString()));
234+
} else if (plugin.getUserManager().getDataManager().isBoolean(columnName)) {
235+
rCol = new Column(columnName, DataType.BOOLEAN);
236+
rCol.setValue(new DataValueBoolean(Boolean.valueOf(rs.getString(i))));
237+
} else {
238+
rCol = new Column(columnName, DataType.STRING);
239+
240+
if ("uuid".equalsIgnoreCase(columnName)) {
241+
// MySQL stores as string; Postgres might store native UUID, handle both
242+
if (dbType == DbType.POSTGRESQL) {
243+
Object obj = rs.getObject(i);
244+
if (obj instanceof java.util.UUID) {
245+
uuid = (java.util.UUID) obj;
246+
uuidStrForSeek = uuid.toString();
247+
rCol.setValue(new DataValueString(uuidStrForSeek));
248+
} else {
249+
String s = rs.getString(i);
250+
uuidStrForSeek = s;
251+
if (s != null && !s.isEmpty() && !"null".equalsIgnoreCase(s)) {
252+
uuid = UUID.fromString(s);
253+
}
254+
rCol.setValue(new DataValueString(s));
255+
}
254256
} else {
255257
String s = rs.getString(i);
258+
uuidStrForSeek = s;
256259
if (s != null && !s.isEmpty() && !"null".equalsIgnoreCase(s)) {
257260
uuid = UUID.fromString(s);
258-
rCol.setValue(new DataValueString(s));
259-
} else {
260-
rCol.setValue(new DataValueString(s));
261261
}
262+
rCol.setValue(new DataValueString(s));
262263
}
263264
} else {
264-
String s = rs.getString(i);
265-
if (s != null && !s.isEmpty() && !"null".equalsIgnoreCase(s)) {
266-
uuid = UUID.fromString(s);
267-
}
268-
rCol.setValue(new DataValueString(s));
265+
rCol.setValue(new DataValueString(rs.getString(i)));
269266
}
270-
} else {
271-
rCol.setValue(new DataValueString(rs.getString(i)));
272267
}
273-
}
274268

275-
cols.add(rCol);
276-
}
269+
cols.add(rCol);
270+
}
277271

278-
if (uuid != null) {
279-
processed++;
280-
perUser.accept(uuid, cols);
272+
if (uuid != null && uuidStrForSeek != null) {
273+
rowsThisPage++;
274+
processed++;
275+
lastUuid = uuidStrForSeek; // advance seek cursor
276+
perUser.accept(uuid, cols);
277+
}
281278
}
282-
// allow GC of cols for each row after callback returns
283279
}
280+
281+
} catch (SQLException e) {
282+
debug(e);
283+
break;
284284
}
285-
} catch (SQLException e) {
286-
debug(e);
287-
} finally {
288-
if (onFinished != null) {
289-
onFinished.accept(processed);
285+
286+
// no more rows
287+
if (rowsThisPage == 0) {
288+
break;
290289
}
291290
}
291+
292+
if (onFinished != null) {
293+
onFinished.accept(processed);
294+
}
292295
}
293296

294297
// -------------------------

0 commit comments

Comments
 (0)