mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-09-05 10:19:15 +00:00
fix(compaction): use DB count for threshold check after server restart
MaybeCompact relied on RAM count which resets to 0 on restart (LoadFromDB is a no-op). Messages accumulated in DB but never triggered compaction. Now falls back to CountByKey DB query when RAM count is below threshold.
This commit is contained in:
@@ -24,6 +24,7 @@ type CompactionConfig struct {
|
||||
|
||||
// MaybeCompact checks if compaction is needed for a history key and triggers it in background.
|
||||
// Called from Record() after appending. Thread-safe via sync.Map compaction guard.
|
||||
// Uses DB count (not RAM count) to correctly detect threshold after server restarts.
|
||||
func (ph *PendingHistory) MaybeCompact(historyKey string, currentCount int, cfg *CompactionConfig) {
|
||||
if ph.store == nil || cfg == nil || cfg.Provider == nil {
|
||||
return
|
||||
@@ -32,9 +33,18 @@ func (ph *PendingHistory) MaybeCompact(historyKey string, currentCount int, cfg
|
||||
if threshold <= 0 {
|
||||
threshold = DefaultGroupHistoryLimit
|
||||
}
|
||||
|
||||
// RAM count may be stale after restart (LoadFromDB doesn't warm full history).
|
||||
// If RAM says below threshold, ask DB for the real count.
|
||||
if currentCount <= threshold {
|
||||
return
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
dbCount, err := ph.store.CountByKey(ctx, ph.channelName, historyKey)
|
||||
if err != nil || dbCount <= threshold {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Guard: only one compaction per key at a time
|
||||
if _, loaded := ph.compacting.LoadOrStore(historyKey, true); loaded {
|
||||
return
|
||||
|
||||
@@ -55,6 +55,9 @@ type PendingMessageStore interface {
|
||||
// CountAll returns the total number of pending messages across all groups.
|
||||
CountAll(ctx context.Context) (int64, error)
|
||||
|
||||
// CountByKey returns the number of pending messages for a specific channel+historyKey.
|
||||
CountByKey(ctx context.Context, channelName, historyKey string) (int, error)
|
||||
|
||||
// ResolveGroupTitles looks up chat_title from session metadata for each group.
|
||||
// Returns a map of "channel_name:history_key" → title. Used only by the UI layer.
|
||||
ResolveGroupTitles(ctx context.Context, groups []PendingMessageGroup) (map[string]string, error)
|
||||
|
||||
@@ -191,6 +191,15 @@ func (s *PGPendingMessageStore) CountAll(ctx context.Context) (int64, error) {
|
||||
return count, err
|
||||
}
|
||||
|
||||
func (s *PGPendingMessageStore) CountByKey(ctx context.Context, channelName, historyKey string) (int, error) {
|
||||
var count int
|
||||
err := s.db.QueryRowContext(ctx,
|
||||
`SELECT COUNT(*) FROM channel_pending_messages WHERE channel_name = $1 AND history_key = $2`,
|
||||
channelName, historyKey,
|
||||
).Scan(&count)
|
||||
return count, err
|
||||
}
|
||||
|
||||
func (s *PGPendingMessageStore) ResolveGroupTitles(ctx context.Context, groups []store.PendingMessageGroup) (map[string]string, error) {
|
||||
if len(groups) == 0 {
|
||||
return nil, nil
|
||||
|
||||
Reference in New Issue
Block a user