You've already forked Arcturus-Morningstar-Extended
mirror of
https://github.com/duckietm/Arcturus-Morningstar-Extended.git
synced 2026-06-20 23:36:19 +00:00
🆙 Database performance fixes
1. HabboManager - O(1) username lookup Before: getHabbo(String) held a synchronized lock and iterated ALL online users every call After: Secondary ConcurrentHashMap<String, Habbo> keyed by lowercase username → instant get() lookup, no lock contention 2. ItemsComponent - Batch DB saves Before: dispose() spawned a separate thread per dirty item, each opening its own DB connection After: Single connection, JDBC addBatch()/executeBatch() for both UPDATE and DELETE, flushed every 100 items. A user with 500 dirty items now does 5 batch executions instead of 500 thread spawns + 500 connections. 3. AcceptFriendRequestEvent - N+1 elimination Before: For each offline user: query 1 (getOfflineHabboInfo by ID) → query 2 (load full Habbo by username) = 2 queries × up to 100 users = 200 queries After: Single query by user ID directly = 1 query × up to 100 users = 100 queries (50% reduction) 4. RoomManager - Direct HashMap lookup Before: getCategory(int id) iterated all categories checking getId() == id even though the map is already keyed by ID After: Direct roomCategories.get(id) → O(1) instead of O(n)
This commit is contained in:
@@ -179,12 +179,7 @@ public class RoomManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public RoomCategory getCategory(int id) {
|
public RoomCategory getCategory(int id) {
|
||||||
for (RoomCategory category : this.roomCategories.values()) {
|
return this.roomCategories.get(id);
|
||||||
if (category.getId() == id)
|
|
||||||
return category;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public RoomCategory getCategory(String name) {
|
public RoomCategory getCategory(String name) {
|
||||||
@@ -220,15 +215,8 @@ public class RoomManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasCategory(int categoryId, Habbo habbo) {
|
public boolean hasCategory(int categoryId, Habbo habbo) {
|
||||||
for (RoomCategory category : this.roomCategories.values()) {
|
RoomCategory category = this.roomCategories.get(categoryId);
|
||||||
if (category.getId() == categoryId) {
|
return category != null && category.getMinRank() <= habbo.getHabboInfo().getRank().getId();
|
||||||
if (category.getMinRank() <= habbo.getHabboInfo().getRank().getId()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public THashMap<Integer, RoomCategory> getRoomCategories() {
|
public THashMap<Integer, RoomCategory> getRoomCategories() {
|
||||||
|
|||||||
@@ -35,11 +35,13 @@ public class HabboManager {
|
|||||||
public static boolean NAMECHANGE_ENABLED = false;
|
public static boolean NAMECHANGE_ENABLED = false;
|
||||||
|
|
||||||
private final ConcurrentHashMap<Integer, Habbo> onlineHabbos;
|
private final ConcurrentHashMap<Integer, Habbo> onlineHabbos;
|
||||||
|
private final ConcurrentHashMap<String, Habbo> onlineHabbosByName;
|
||||||
|
|
||||||
public HabboManager() {
|
public HabboManager() {
|
||||||
long millis = System.currentTimeMillis();
|
long millis = System.currentTimeMillis();
|
||||||
|
|
||||||
this.onlineHabbos = new ConcurrentHashMap<>();
|
this.onlineHabbos = new ConcurrentHashMap<>();
|
||||||
|
this.onlineHabbosByName = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
LOGGER.info("Habbo Manager -> Loaded! ({} MS)", System.currentTimeMillis() - millis);
|
LOGGER.info("Habbo Manager -> Loaded! ({} MS)", System.currentTimeMillis() - millis);
|
||||||
}
|
}
|
||||||
@@ -80,10 +82,12 @@ public class HabboManager {
|
|||||||
|
|
||||||
public void addHabbo(Habbo habbo) {
|
public void addHabbo(Habbo habbo) {
|
||||||
this.onlineHabbos.put(habbo.getHabboInfo().getId(), habbo);
|
this.onlineHabbos.put(habbo.getHabboInfo().getId(), habbo);
|
||||||
|
this.onlineHabbosByName.put(habbo.getHabboInfo().getUsername().toLowerCase(), habbo);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeHabbo(Habbo habbo) {
|
public void removeHabbo(Habbo habbo) {
|
||||||
this.onlineHabbos.remove(habbo.getHabboInfo().getId());
|
this.onlineHabbos.remove(habbo.getHabboInfo().getId());
|
||||||
|
this.onlineHabbosByName.remove(habbo.getHabboInfo().getUsername().toLowerCase());
|
||||||
}
|
}
|
||||||
|
|
||||||
public Habbo getHabbo(int id) {
|
public Habbo getHabbo(int id) {
|
||||||
@@ -91,14 +95,7 @@ public class HabboManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Habbo getHabbo(String username) {
|
public Habbo getHabbo(String username) {
|
||||||
synchronized (this.onlineHabbos) {
|
return this.onlineHabbosByName.get(username.toLowerCase());
|
||||||
for (Map.Entry<Integer, Habbo> map : this.onlineHabbos.entrySet()) {
|
|
||||||
if (map.getValue().getHabboInfo().getUsername().equalsIgnoreCase(username))
|
|
||||||
return map.getValue();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Habbo loadHabbo(String sso) {
|
public Habbo loadHabbo(String sso) {
|
||||||
@@ -178,14 +175,12 @@ public class HabboManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void sendPacketToHabbosWithPermission(ServerMessage message, String perm) {
|
public void sendPacketToHabbosWithPermission(ServerMessage message, String perm) {
|
||||||
synchronized (this.onlineHabbos) {
|
|
||||||
for (Habbo habbo : this.onlineHabbos.values()) {
|
for (Habbo habbo : this.onlineHabbos.values()) {
|
||||||
if (habbo.hasPermission(perm)) {
|
if (habbo.hasPermission(perm)) {
|
||||||
habbo.getClient().sendResponse(message);
|
habbo.getClient().sendResponse(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public ConcurrentHashMap<Integer, Habbo> getOnlineHabbos() {
|
public ConcurrentHashMap<Integer, Habbo> getOnlineHabbos() {
|
||||||
return this.onlineHabbos;
|
return this.onlineHabbos;
|
||||||
|
|||||||
@@ -159,14 +159,63 @@ public class ItemsComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!this.items.isEmpty()) {
|
if (!this.items.isEmpty()) {
|
||||||
|
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection()) {
|
||||||
|
try (PreparedStatement updateStmt = connection.prepareStatement(
|
||||||
|
"UPDATE items SET user_id = ?, room_id = ?, wall_pos = ?, x = ?, y = ?, z = ?, rot = ?, extra_data = ?, limited_data = ? WHERE id = ?")) {
|
||||||
|
try (PreparedStatement deleteStmt = connection.prepareStatement(
|
||||||
|
"DELETE FROM items WHERE id = ?")) {
|
||||||
|
|
||||||
|
int updateCount = 0;
|
||||||
|
int deleteCount = 0;
|
||||||
|
|
||||||
for (int i = this.items.size(); i-- > 0; ) {
|
for (int i = this.items.size(); i-- > 0; ) {
|
||||||
try {
|
try {
|
||||||
items.advance();
|
items.advance();
|
||||||
} catch (NoSuchElementException e) {
|
} catch (NoSuchElementException e) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (items.value().needsUpdate())
|
|
||||||
Emulator.getThreading().run(items.value());
|
HabboItem item = items.value();
|
||||||
|
if (item.needsDelete()) {
|
||||||
|
deleteStmt.setInt(1, item.getId());
|
||||||
|
deleteStmt.addBatch();
|
||||||
|
deleteCount++;
|
||||||
|
item.needsUpdate(false);
|
||||||
|
item.needsDelete(false);
|
||||||
|
} else if (item.needsUpdate()) {
|
||||||
|
updateStmt.setInt(1, item.getUserId());
|
||||||
|
updateStmt.setInt(2, item.getRoomId());
|
||||||
|
updateStmt.setString(3, item.getWallPosition());
|
||||||
|
updateStmt.setInt(4, item.getX());
|
||||||
|
updateStmt.setInt(5, item.getY());
|
||||||
|
updateStmt.setDouble(6, item.getZ());
|
||||||
|
updateStmt.setInt(7, item.getRotation());
|
||||||
|
updateStmt.setString(8, item.getExtradata());
|
||||||
|
updateStmt.setString(9, item.getLimitedStack() + ":" + item.getLimitedSells());
|
||||||
|
updateStmt.setInt(10, item.getId());
|
||||||
|
updateStmt.addBatch();
|
||||||
|
updateCount++;
|
||||||
|
item.needsUpdate(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (updateCount > 0 && updateCount % 100 == 0) {
|
||||||
|
updateStmt.executeBatch();
|
||||||
|
}
|
||||||
|
if (deleteCount > 0 && deleteCount % 100 == 0) {
|
||||||
|
deleteStmt.executeBatch();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (deleteCount % 100 != 0) {
|
||||||
|
deleteStmt.executeBatch();
|
||||||
|
}
|
||||||
|
if (updateCount % 100 != 0) {
|
||||||
|
updateStmt.executeBatch();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (SQLException e) {
|
||||||
|
LOGGER.error("Caught SQL exception during batch item save", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+3
-13
@@ -3,7 +3,6 @@ package com.eu.habbo.messages.incoming.friends;
|
|||||||
import com.eu.habbo.Emulator;
|
import com.eu.habbo.Emulator;
|
||||||
import com.eu.habbo.habbohotel.messenger.Messenger;
|
import com.eu.habbo.habbohotel.messenger.Messenger;
|
||||||
import com.eu.habbo.habbohotel.users.Habbo;
|
import com.eu.habbo.habbohotel.users.Habbo;
|
||||||
import com.eu.habbo.habbohotel.users.HabboInfo;
|
|
||||||
import com.eu.habbo.messages.incoming.MessageHandler;
|
import com.eu.habbo.messages.incoming.MessageHandler;
|
||||||
import com.eu.habbo.messages.outgoing.friends.FriendRequestErrorComposer;
|
import com.eu.habbo.messages.outgoing.friends.FriendRequestErrorComposer;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@@ -14,7 +13,6 @@ import java.sql.PreparedStatement;
|
|||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
|
|
||||||
import static com.eu.habbo.habbohotel.users.HabboManager.getOfflineHabboInfo;
|
|
||||||
|
|
||||||
public class AcceptFriendRequestEvent extends MessageHandler {
|
public class AcceptFriendRequestEvent extends MessageHandler {
|
||||||
private static final Logger LOGGER = LoggerFactory.getLogger(AcceptFriendRequestEvent.class);
|
private static final Logger LOGGER = LoggerFactory.getLogger(AcceptFriendRequestEvent.class);
|
||||||
@@ -44,18 +42,10 @@ public class AcceptFriendRequestEvent extends MessageHandler {
|
|||||||
Habbo target = Emulator.getGameEnvironment().getHabboManager().getHabbo(userId);
|
Habbo target = Emulator.getGameEnvironment().getHabboManager().getHabbo(userId);
|
||||||
|
|
||||||
if(target == null) {
|
if(target == null) {
|
||||||
HabboInfo habboInfo = getOfflineHabboInfo(userId);
|
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection(); PreparedStatement statement = connection.prepareStatement("SELECT users.*, users_settings.block_friendrequests FROM users INNER JOIN users_settings ON users.id = users_settings.user_id WHERE users.id = ? LIMIT 1")) {
|
||||||
|
statement.setInt(1, userId);
|
||||||
if(habboInfo == null) {
|
|
||||||
this.client.sendResponse(new FriendRequestErrorComposer(FriendRequestErrorComposer.TARGET_NOT_FOUND));
|
|
||||||
this.client.getHabbo().getMessenger().deleteFriendRequests(userId, this.client.getHabbo().getHabboInfo().getId());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection(); PreparedStatement statement = connection.prepareStatement("SELECT users.*, users_settings.block_friendrequests FROM users INNER JOIN users_settings ON users.id = users_settings.user_id WHERE username = ? LIMIT 1")) {
|
|
||||||
statement.setString(1, habboInfo.getUsername());
|
|
||||||
try (ResultSet set = statement.executeQuery()) {
|
try (ResultSet set = statement.executeQuery()) {
|
||||||
while (set.next()) {
|
if (set.next()) {
|
||||||
target = new Habbo(set);
|
target = new Habbo(set);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user