mirror of
https://github.com/Mezeporta/Erupe.git
synced 2026-03-22 07:32:32 +01:00
perf(channelserver): move UserBinary and minidata to memory-only
UserBinary type1-5 and EnhancedMinidata are transient session state resent by the client on every login. Persisting them to the DB on every set was unnecessary I/O. Both are now served exclusively from server-scoped in-memory maps (userBinaryParts, minidataParts). Includes a schema migration to drop the now-unused type2/type3 columns from user_binary and minidata column from characters. Ref #158
This commit is contained in:
@@ -0,0 +1,7 @@
|
|||||||
|
-- Drop transient binary columns that are now memory-only.
|
||||||
|
-- UserBinary type2/type3 and characters.minidata are session state
|
||||||
|
-- resent by the client on every login; they do not need persistence.
|
||||||
|
|
||||||
|
ALTER TABLE user_binary DROP COLUMN IF EXISTS type2;
|
||||||
|
ALTER TABLE user_binary DROP COLUMN IF EXISTS type3;
|
||||||
|
ALTER TABLE characters DROP COLUMN IF EXISTS minidata;
|
||||||
@@ -211,11 +211,12 @@ func handleMsgMhfUseUdShopCoin(s *Session, p mhfpacket.MHFPacket) {}
|
|||||||
|
|
||||||
func handleMsgMhfGetEnhancedMinidata(s *Session, p mhfpacket.MHFPacket) {
|
func handleMsgMhfGetEnhancedMinidata(s *Session, p mhfpacket.MHFPacket) {
|
||||||
pkt := p.(*mhfpacket.MsgMhfGetEnhancedMinidata)
|
pkt := p.(*mhfpacket.MsgMhfGetEnhancedMinidata)
|
||||||
// this looks to be the detailed chunk of information you can pull up on players in town
|
|
||||||
var data []byte
|
s.server.minidataLock.RLock()
|
||||||
err := s.server.db.QueryRow("SELECT minidata FROM characters WHERE id = $1", pkt.CharID).Scan(&data)
|
data, ok := s.server.minidataParts[pkt.CharID]
|
||||||
if err != nil {
|
s.server.minidataLock.RUnlock()
|
||||||
s.logger.Error("Failed to load minidata")
|
|
||||||
|
if !ok {
|
||||||
data = make([]byte, 1)
|
data = make([]byte, 1)
|
||||||
}
|
}
|
||||||
doAckBufSucceed(s, pkt.AckHandle, data)
|
doAckBufSucceed(s, pkt.AckHandle, data)
|
||||||
@@ -224,10 +225,11 @@ func handleMsgMhfGetEnhancedMinidata(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
func handleMsgMhfSetEnhancedMinidata(s *Session, p mhfpacket.MHFPacket) {
|
func handleMsgMhfSetEnhancedMinidata(s *Session, p mhfpacket.MHFPacket) {
|
||||||
pkt := p.(*mhfpacket.MsgMhfSetEnhancedMinidata)
|
pkt := p.(*mhfpacket.MsgMhfSetEnhancedMinidata)
|
||||||
dumpSaveData(s, pkt.RawDataPayload, "minidata")
|
dumpSaveData(s, pkt.RawDataPayload, "minidata")
|
||||||
_, err := s.server.db.Exec("UPDATE characters SET minidata=$1 WHERE id=$2", pkt.RawDataPayload, s.charID)
|
|
||||||
if err != nil {
|
s.server.minidataLock.Lock()
|
||||||
s.logger.Error("Failed to save minidata", zap.Error(err))
|
s.server.minidataParts[s.charID] = pkt.RawDataPayload
|
||||||
}
|
s.server.minidataLock.Unlock()
|
||||||
|
|
||||||
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
|
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
package channelserver
|
package channelserver
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"erupe-ce/network/mhfpacket"
|
"erupe-ce/network/mhfpacket"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
@@ -21,42 +19,21 @@ func handleMsgSysSetUserBinary(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
s.server.userBinaryParts[userBinaryPartID{charID: s.charID, index: pkt.BinaryType}] = pkt.RawDataPayload
|
s.server.userBinaryParts[userBinaryPartID{charID: s.charID, index: pkt.BinaryType}] = pkt.RawDataPayload
|
||||||
s.server.userBinaryPartsLock.Unlock()
|
s.server.userBinaryPartsLock.Unlock()
|
||||||
|
|
||||||
var exists []byte
|
s.server.BroadcastMHF(&mhfpacket.MsgSysNotifyUserBinary{
|
||||||
err := s.server.db.QueryRow("SELECT type2 FROM user_binary WHERE id=$1", s.charID).Scan(&exists)
|
|
||||||
if err != nil {
|
|
||||||
if _, err := s.server.db.Exec("INSERT INTO user_binary (id) VALUES ($1)", s.charID); err != nil {
|
|
||||||
s.logger.Error("Failed to insert user binary", zap.Error(err))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := s.server.db.Exec(fmt.Sprintf("UPDATE user_binary SET type%d=$1 WHERE id=$2", pkt.BinaryType), pkt.RawDataPayload, s.charID); err != nil {
|
|
||||||
s.logger.Error("Failed to update user binary", zap.Error(err))
|
|
||||||
}
|
|
||||||
|
|
||||||
msg := &mhfpacket.MsgSysNotifyUserBinary{
|
|
||||||
CharID: s.charID,
|
CharID: s.charID,
|
||||||
BinaryType: pkt.BinaryType,
|
BinaryType: pkt.BinaryType,
|
||||||
}
|
}, s)
|
||||||
|
|
||||||
s.server.BroadcastMHF(msg, s)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleMsgSysGetUserBinary(s *Session, p mhfpacket.MHFPacket) {
|
func handleMsgSysGetUserBinary(s *Session, p mhfpacket.MHFPacket) {
|
||||||
pkt := p.(*mhfpacket.MsgSysGetUserBinary)
|
pkt := p.(*mhfpacket.MsgSysGetUserBinary)
|
||||||
|
|
||||||
// Try to get the data.
|
|
||||||
s.server.userBinaryPartsLock.RLock()
|
s.server.userBinaryPartsLock.RLock()
|
||||||
defer s.server.userBinaryPartsLock.RUnlock()
|
|
||||||
data, ok := s.server.userBinaryParts[userBinaryPartID{charID: pkt.CharID, index: pkt.BinaryType}]
|
data, ok := s.server.userBinaryParts[userBinaryPartID{charID: pkt.CharID, index: pkt.BinaryType}]
|
||||||
|
s.server.userBinaryPartsLock.RUnlock()
|
||||||
|
|
||||||
// If we can't get the real data, try to get it from the database.
|
|
||||||
if !ok {
|
if !ok {
|
||||||
err := s.server.db.QueryRow(fmt.Sprintf("SELECT type%d FROM user_binary WHERE id=$1", pkt.BinaryType), pkt.CharID).Scan(&data)
|
doAckBufFail(s, pkt.AckHandle, make([]byte, 4))
|
||||||
if err != nil {
|
|
||||||
doAckBufFail(s, pkt.AckHandle, make([]byte, 4))
|
|
||||||
} else {
|
|
||||||
doAckBufSucceed(s, pkt.AckHandle, data)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
doAckBufSucceed(s, pkt.AckHandle, data)
|
doAckBufSucceed(s, pkt.AckHandle, data)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -81,23 +81,23 @@ func TestHandleMsgSysGetUserBinary_NotInCache(t *testing.T) {
|
|||||||
server.userBinaryParts = make(map[userBinaryPartID][]byte)
|
server.userBinaryParts = make(map[userBinaryPartID][]byte)
|
||||||
session := createMockSession(1, server)
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
// Don't populate cache - will fall back to DB (which is nil in test)
|
|
||||||
pkt := &mhfpacket.MsgSysGetUserBinary{
|
pkt := &mhfpacket.MsgSysGetUserBinary{
|
||||||
AckHandle: 12345,
|
AckHandle: 12345,
|
||||||
CharID: 100,
|
CharID: 100,
|
||||||
BinaryType: 1,
|
BinaryType: 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
// This will panic when trying to access nil db, which is expected
|
|
||||||
// in the test environment without database setup
|
|
||||||
defer func() {
|
|
||||||
if r := recover(); r != nil {
|
|
||||||
// Expected - no database in test
|
|
||||||
t.Log("Expected panic due to nil database in test")
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
handleMsgSysGetUserBinary(session, pkt)
|
handleMsgSysGetUserBinary(session, pkt)
|
||||||
|
|
||||||
|
// Should return a fail ACK (no DB fallback, just cache miss)
|
||||||
|
select {
|
||||||
|
case p := <-session.sendPackets:
|
||||||
|
if len(p.data) == 0 {
|
||||||
|
t.Error("Response packet should have data")
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
t.Error("No response packet queued")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUserBinaryPartID_AsMapKey(t *testing.T) {
|
func TestUserBinaryPartID_AsMapKey(t *testing.T) {
|
||||||
|
|||||||
@@ -584,6 +584,7 @@ func createTestServerWithDB(t *testing.T, db *sqlx.DB) *Server {
|
|||||||
sessions: make(map[net.Conn]*Session),
|
sessions: make(map[net.Conn]*Session),
|
||||||
stages: make(map[string]*Stage),
|
stages: make(map[string]*Stage),
|
||||||
userBinaryParts: make(map[userBinaryPartID][]byte),
|
userBinaryParts: make(map[userBinaryPartID][]byte),
|
||||||
|
minidataParts: make(map[uint32][]byte),
|
||||||
semaphore: make(map[string]*Semaphore),
|
semaphore: make(map[string]*Semaphore),
|
||||||
erupeConfig: _config.ErupeConfig,
|
erupeConfig: _config.ErupeConfig,
|
||||||
isShuttingDown: false,
|
isShuttingDown: false,
|
||||||
|
|||||||
@@ -60,6 +60,10 @@ type Server struct {
|
|||||||
userBinaryPartsLock sync.RWMutex
|
userBinaryPartsLock sync.RWMutex
|
||||||
userBinaryParts map[userBinaryPartID][]byte
|
userBinaryParts map[userBinaryPartID][]byte
|
||||||
|
|
||||||
|
// EnhancedMinidata
|
||||||
|
minidataLock sync.RWMutex
|
||||||
|
minidataParts map[uint32][]byte
|
||||||
|
|
||||||
// Semaphore
|
// Semaphore
|
||||||
semaphoreLock sync.RWMutex
|
semaphoreLock sync.RWMutex
|
||||||
semaphore map[string]*Semaphore
|
semaphore map[string]*Semaphore
|
||||||
@@ -89,6 +93,7 @@ func NewServer(config *Config) *Server {
|
|||||||
sessions: make(map[net.Conn]*Session),
|
sessions: make(map[net.Conn]*Session),
|
||||||
stages: make(map[string]*Stage),
|
stages: make(map[string]*Stage),
|
||||||
userBinaryParts: make(map[userBinaryPartID][]byte),
|
userBinaryParts: make(map[userBinaryPartID][]byte),
|
||||||
|
minidataParts: make(map[uint32][]byte),
|
||||||
semaphore: make(map[string]*Semaphore),
|
semaphore: make(map[string]*Semaphore),
|
||||||
semaphoreIndex: 7,
|
semaphoreIndex: 7,
|
||||||
discordBot: config.DiscordBot,
|
discordBot: config.DiscordBot,
|
||||||
|
|||||||
Reference in New Issue
Block a user