some fixes

This commit is contained in:
Radek Davidek 2026-03-20 13:34:12 +01:00
parent 6f8f28fc28
commit 2207390eb2
5 changed files with 178 additions and 48 deletions

4
.gitignore vendored
View File

@ -5,3 +5,7 @@ target
.classpath
.project
.claude
.vscode
.DS_Store
*.log
plans

View File

@ -43,7 +43,7 @@
<createDependencyReducedPom>false</createDependencyReducedPom>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>cz.kamma.fabka.httpserver.HttpServerApplication</mainClass>
<mainClass>cz.kamma.fabka.app.HttpServerApplication</mainClass>
</transformer>
</transformers>
</configuration>

View File

@ -9,6 +9,7 @@ import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import cz.kamma.fabka.repository.model.ChatLine;
import cz.kamma.fabka.repository.model.ChatVoteStats;
@ -83,23 +84,26 @@ public class ChatRepository {
if (chatId <= 0) {
return new ChatVoteStats("", "");
}
long startTime = System.nanoTime();
ChatVoteStats result = null;
try (Connection conn = DriverManager.getConnection(jdbcUrl, jdbcUser, jdbcPassword);
PreparedStatement ps = conn.prepareStatement(CHAT_VOTES_USERS_SQL)) {
ps.setLong(1, chatId);
ps.setLong(2, chatId);
try (ResultSet rs = ps.executeQuery()) {
if (!rs.next()) {
return new ChatVoteStats("", "");
if (rs.next()) {
result = new ChatVoteStats(
valueOrDefault(rs.getString("thumbup"), ""),
valueOrDefault(rs.getString("thumbdown"), "")
);
}
return new ChatVoteStats(
valueOrDefault(rs.getString("thumbup"), ""),
valueOrDefault(rs.getString("thumbdown"), "")
);
}
} catch (Exception ex) {
ex.printStackTrace();
return new ChatVoteStats("", "");
}
long durationMs = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime);
System.out.println("[DEBUG] CHAT_VOTES_USERS_SQL: chatId=" + chatId + ", duration=" + durationMs + "ms");
return result != null ? result : new ChatVoteStats("", "");
}
public void addChatMessage(long userId, String message) {
@ -123,6 +127,7 @@ public class ChatRepository {
return lines;
}
int safeLimit = limit <= 0 ? 40 : limit;
long startTime = System.nanoTime();
try (Connection conn = DriverManager.getConnection(jdbcUrl, jdbcUser, jdbcPassword);
PreparedStatement ps = conn.prepareStatement(CHAT_LINES_SQL)) {
ps.setLong(1, currentUserId);
@ -146,6 +151,8 @@ public class ChatRepository {
} catch (Exception ex) {
ex.printStackTrace();
}
long durationMs = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime);
System.out.println("[DEBUG] CHAT_LINES_SQL: userId=" + currentUserId + ", limit=" + safeLimit + ", lines=" + lines.size() + ", duration=" + durationMs + "ms");
return lines;
}

View File

@ -16,6 +16,7 @@ import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import cz.kamma.fabka.repository.model.AttachmentData;
import cz.kamma.fabka.repository.model.ForumAttachment;
@ -30,6 +31,16 @@ public class ForumRepository {
private static final ZoneId APP_ZONE = ZoneId.of("Europe/Prague");
private static final String FORUM_DETAIL_SQL =
"SELECT id, name, description, countdown FROM forum WHERE id=? AND active=1 LIMIT 1";
private static final String FORUM_MESSAGES_SQL_DEBUG =
"SELECT fi.id, fi.text, fi.created, fi.quoteitem, fi.sticky, ua.id AS author_id, ua.username, ua.created AS author_created, ua.city, " +
" (SELECT COUNT(*) FROM forum_items fi2 WHERE fi2.createdby=ua.id AND fi2.deleted=0) AS author_posts, " +
" COALESCE((SELECT SUM(votevalue) FROM voting v WHERE v.forumitemid=fi.id), 0) AS vvalue, " +
" (SELECT COUNT(*) FROM voting v WHERE v.forumitemid=fi.id AND v.votevalue=1) AS vote_yes, " +
" (SELECT COUNT(*) FROM voting v WHERE v.forumitemid=fi.id AND v.votevalue=-1) AS vote_no, " +
" (SELECT GROUP_CONCAT(ua2.username SEPARATOR ',') FROM voting v JOIN user_accounts ua2 ON ua2.id=v.voteby WHERE v.forumitemid=fi.id AND v.votevalue=1) AS vote_yes_users, " +
" (SELECT GROUP_CONCAT(ua2.username SEPARATOR ',') FROM voting v JOIN user_accounts ua2 ON ua2.id=v.voteby WHERE v.forumitemid=fi.id AND v.votevalue=-1) AS vote_no_users " +
"FROM forum_items fi JOIN user_accounts ua ON ua.id=fi.createdby " +
"WHERE fi.forumid=? AND fi.deleted=0 ORDER BY fi.created DESC";
private static final String QUOTED_ITEM_SQL =
"SELECT fi.text, ua.username FROM forum_items fi JOIN user_accounts ua ON ua.id=fi.createdby WHERE fi.id=? LIMIT 1";
private static final String ATTACHMENTS_SQL =
@ -104,6 +115,7 @@ public class ForumRepository {
public List<ForumSummary> listActiveForums() {
List<ForumSummary> forums = new ArrayList<>();
long startTime = System.nanoTime();
try (Connection conn = DriverManager.getConnection(jdbcUrl, jdbcUser, jdbcPassword);
PreparedStatement ps = conn.prepareStatement(FORUM_SQL);
ResultSet rs = ps.executeQuery()) {
@ -122,6 +134,8 @@ public class ForumRepository {
} catch (Exception ex) {
ex.printStackTrace();
}
long durationMs = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime);
System.out.println("[DEBUG] FORUM_SQL: forums=" + forums.size() + ", duration=" + durationMs + "ms");
return forums;
}
@ -144,6 +158,7 @@ public class ForumRepository {
if (userId <= 0) {
return result;
}
long startTime = System.nanoTime();
try (Connection conn = DriverManager.getConnection(jdbcUrl, jdbcUser, jdbcPassword);
PreparedStatement ps = conn.prepareStatement(NEW_MESSAGES_COUNT_SQL)) {
ps.setLong(1, userId);
@ -159,6 +174,8 @@ public class ForumRepository {
} catch (Exception ex) {
ex.printStackTrace();
}
long durationMs = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime);
System.out.println("[DEBUG] NEW_MESSAGES_COUNT_SQL: userId=" + userId + ", forums=" + result.size() + ", duration=" + durationMs + "ms");
return result;
}
@ -166,24 +183,27 @@ public class ForumRepository {
if (forumId <= 0) {
return null;
}
long startTime = System.nanoTime();
ForumDetail result = null;
try (Connection conn = DriverManager.getConnection(jdbcUrl, jdbcUser, jdbcPassword);
PreparedStatement ps = conn.prepareStatement(FORUM_DETAIL_SQL)) {
ps.setLong(1, forumId);
try (ResultSet rs = ps.executeQuery()) {
if (!rs.next()) {
return null;
if (rs.next()) {
result = new ForumDetail(
rs.getLong("id"),
rs.getString("name"),
rs.getString("description"),
rs.getString("countdown")
);
}
return new ForumDetail(
rs.getLong("id"),
rs.getString("name"),
rs.getString("description"),
rs.getString("countdown")
);
}
} catch (Exception ex) {
ex.printStackTrace();
return null;
}
long durationMs = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime);
System.out.println("[DEBUG] FORUM_DETAIL_SQL: forumId=" + forumId + ", duration=" + durationMs + "ms");
return result;
}
public List<ForumMessage> listMessagesByForumId(long forumId) {
@ -191,46 +211,103 @@ public class ForumRepository {
if (forumId <= 0) {
return messages;
}
if (!loadMessages(messages, forumId, FORUM_MESSAGES_SQL)) {
long startTime = System.nanoTime();
if (!loadMessages(messages, forumId, FORUM_MESSAGES_SQL_DEBUG)) {
loadMessages(messages, forumId, FORUM_MESSAGES_SQL_NO_STICKY);
}
long durationMs = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime);
System.out.println("[DEBUG] FORUM_MESSAGES_SQL: forumId=" + forumId + ", rows=" + messages.size() + ", duration=" + durationMs + "ms");
return messages;
}
private boolean loadMessages(List<ForumMessage> out, long forumId, String sql) {
out.clear();
long queryStartTime = System.nanoTime();
// First, fetch all messages and collect item IDs for batch attachment loading
List<Long> itemIds = new ArrayList<>();
List<Timestamp> createdTimes = new ArrayList<>();
List<Long> authorIds = new ArrayList<>();
List<String> usernames = new ArrayList<>();
List<String> cities = new ArrayList<>();
List<String> texts = new ArrayList<>();
List<Long> authorPostCounts = new ArrayList<>();
List<Integer> vvalues = new ArrayList<>();
List<Integer> voteYes = new ArrayList<>();
List<Integer> voteNo = new ArrayList<>();
List<String> voteYesUsers = new ArrayList<>();
List<String> voteNoUsers = new ArrayList<>();
List<Long> ids = new ArrayList<>();
List<Long> quoteItemIds = new ArrayList<>();
List<Integer> stickies = new ArrayList<>();
try (Connection conn = DriverManager.getConnection(jdbcUrl, jdbcUser, jdbcPassword);
PreparedStatement ps = conn.prepareStatement(sql)) {
ps.setLong(1, forumId);
try (ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
Timestamp createdTs = rs.getTimestamp("created");
long quoteItemId = rs.getLong("quoteitem");
QuotedTextItem quotedItem = quoteItemId > 0 ? findQuotedItem(quoteItemId) : null;
out.add(new ForumMessage(
rs.getLong("id"),
rs.getLong("author_id"),
valueOrDefault(rs.getString("username"), "N/A"),
formatTs(rs.getTimestamp("author_created")),
valueOrDefault(rs.getString("city"), ""),
rs.getLong("author_posts"),
createdTs == null ? 0L : createdTs.getTime(),
rs.getInt("vvalue"),
rs.getInt("vote_yes"),
rs.getInt("vote_no"),
valueOrDefault(rs.getString("vote_yes_users"), ""),
valueOrDefault(rs.getString("vote_no_users"), ""),
quoteItemId,
quotedItem,
formatTs(createdTs),
valueOrDefault(rs.getString("text"), ""),
listAttachmentsByForumItemId(rs.getLong("id")),
rs.getInt("sticky") == 1
));
ids.add(rs.getLong("id"));
itemIds.add(rs.getLong("id"));
createdTimes.add(rs.getTimestamp("created"));
authorIds.add(rs.getLong("author_id"));
usernames.add(rs.getString("username"));
cities.add(rs.getString("city"));
authorPostCounts.add(rs.getLong("author_posts"));
texts.add(rs.getString("text"));
vvalues.add(rs.getInt("vvalue"));
voteYes.add(rs.getInt("vote_yes"));
voteNo.add(rs.getInt("vote_no"));
voteYesUsers.add(rs.getString("vote_yes_users"));
voteNoUsers.add(rs.getString("vote_no_users"));
quoteItemIds.add(rs.getLong("quoteitem"));
stickies.add(rs.getInt("sticky"));
}
}
// Batch load attachments for all items
long attachmentsStartTime = System.nanoTime();
Map<Long, List<ForumAttachment>> attachmentsByItemId = new LinkedHashMap<>();
if (!itemIds.isEmpty()) {
attachmentsByItemId = loadAttachmentsBatch(itemIds);
}
long attachmentsDurationMs = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - attachmentsStartTime);
System.out.println("[DEBUG] Batch attachment load: " + attachmentsDurationMs + "ms, " + itemIds.size() + " items");
// Build message objects
for (int i = 0; i < ids.size(); i++) {
long messageId = ids.get(i);
long quoteItemId = quoteItemIds.get(i);
QuotedTextItem quotedItem = quoteItemId > 0 ? findQuotedItem(quoteItemId) : null;
List<ForumAttachment> attachments = attachmentsByItemId.getOrDefault(messageId, new ArrayList<>());
out.add(new ForumMessage(
messageId,
authorIds.get(i),
valueOrDefault(usernames.get(i), "N/A"),
formatTs(createdTimes.get(i)),
valueOrDefault(cities.get(i), ""),
authorPostCounts.get(i),
createdTimes.get(i) == null ? 0L : createdTimes.get(i).getTime(),
vvalues.get(i),
voteYes.get(i),
voteNo.get(i),
valueOrDefault(voteYesUsers.get(i), ""),
valueOrDefault(voteNoUsers.get(i), ""),
quoteItemId,
quotedItem,
formatTs(createdTimes.get(i)),
valueOrDefault(texts.get(i), ""),
attachments,
stickies.get(i) == 1
));
}
long totalDurationMs = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - queryStartTime);
System.out.println("[DEBUG] loadMessages completed: total=" + totalDurationMs + "ms, rows=" + out.size());
return true;
} catch (Exception ex) {
System.err.println("[DEBUG] loadMessages ERROR: " + ex.getMessage());
ex.printStackTrace();
return false;
}
}
@ -374,22 +451,25 @@ public class ForumRepository {
if (messageId <= 0) {
return null;
}
long startTime = System.nanoTime();
QuotedTextItem result = null;
try (Connection conn = DriverManager.getConnection(jdbcUrl, jdbcUser, jdbcPassword);
PreparedStatement ps = conn.prepareStatement(QUOTED_ITEM_SQL)) {
ps.setLong(1, messageId);
try (ResultSet rs = ps.executeQuery()) {
if (!rs.next()) {
return null;
if (rs.next()) {
result = new QuotedTextItem(
valueOrDefault(rs.getString("username"), "N/A"),
valueOrDefault(rs.getString("text"), "")
);
}
return new QuotedTextItem(
valueOrDefault(rs.getString("username"), "N/A"),
valueOrDefault(rs.getString("text"), "")
);
}
} catch (Exception ex) {
ex.printStackTrace();
return null;
}
long durationMs = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime);
System.out.println("[DEBUG] QUOTED_ITEM_SQL: messageId=" + messageId + ", duration=" + durationMs + "ms");
return result;
}
public long addReply(long forumId, long userId, String message, Long quoteItem) {
@ -509,6 +589,7 @@ public class ForumRepository {
if (forumItemId <= 0) {
return out;
}
long startTime = System.nanoTime();
try (Connection conn = DriverManager.getConnection(jdbcUrl, jdbcUser, jdbcPassword);
PreparedStatement ps = conn.prepareStatement(ATTACHMENTS_SQL)) {
ps.setLong(1, forumItemId);
@ -525,6 +606,10 @@ public class ForumRepository {
} catch (Exception ex) {
ex.printStackTrace();
}
long durationMs = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime);
if (durationMs > 1) {
System.out.println("[DEBUG] ATTACHMENTS_SQL: forumItemId=" + forumItemId + ", count=" + out.size() + ", duration=" + durationMs + "ms");
}
return out;
}
@ -559,6 +644,36 @@ public class ForumRepository {
}
}
private Map<Long, List<ForumAttachment>> loadAttachmentsBatch(List<Long> itemIds) {
Map<Long, List<ForumAttachment>> result = new LinkedHashMap<>();
if (itemIds.isEmpty()) {
return result;
}
String sql = "SELECT forumitemsid, id, name, ispicture, width, size FROM attachments WHERE forumitemsid IN (" +
String.join(",", java.util.Collections.nCopies(itemIds.size(), "?")) + ") ORDER BY forumitemsid, id";
try (Connection conn = DriverManager.getConnection(jdbcUrl, jdbcUser, jdbcPassword);
PreparedStatement ps = conn.prepareStatement(sql)) {
for (int i = 0; i < itemIds.size(); i++) {
ps.setLong(i + 1, itemIds.get(i));
}
try (ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
long forumItemId = rs.getLong("forumitemsid");
ForumAttachment attachment = new ForumAttachment(
rs.getLong("id"),
valueOrDefault(rs.getString("name"), "attachment"),
rs.getInt("ispicture") == 1,
rs.getInt("width")
);
result.computeIfAbsent(forumItemId, k -> new ArrayList<>()).add(attachment);
}
}
} catch (Exception ex) {
ex.printStackTrace();
}
return result;
}
private static String valueOrDefault(String value, String defaultValue) {
return value == null || value.isBlank() ? defaultValue : value;
}

View File

@ -10,6 +10,7 @@ import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import cz.kamma.fabka.repository.model.PrivateMessageItem;
import cz.kamma.fabka.repository.model.PrivateMessageStats;
@ -56,6 +57,7 @@ public class PrivateMessageRepository {
"where (m.to_user=? or m.from_user=?) and m.deleted=0 and m.reply_to is null " +
"order by " + order;
long startTime = System.nanoTime();
try (Connection conn = DriverManager.getConnection(jdbcUrl, jdbcUser, jdbcPassword);
PreparedStatement ps = conn.prepareStatement(sql)) {
ps.setLong(1, userId);
@ -77,6 +79,8 @@ public class PrivateMessageRepository {
} catch (Exception ex) {
ex.printStackTrace();
}
long durationMs = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime);
System.out.println("[DEBUG] PM listThreads: userId=" + userId + ", threads=" + out.size() + ", duration=" + durationMs + "ms");
return out;
}