P1.4: tenant-индекс очередей (O(1) подсчёт/список вместо скана)

Введён тип QueueStore с индексом accessKey→{queueName}. Мутации очередей идут через
AddQueue/RemoveQueue (create/delete/restore/config/seed/admin), подсчёт и список —
через CountTenantQueues/TenantQueueNames. Устранены O(total) скан-по-префиксу в
countTenantQueues, deleteTenant, listTenantQueues, createTenantQueue. API SyncQueues
(Queues, Lock) сохранён — остальной код не затронут.
This commit is contained in:
Repinoid
2026-10-06 18:36:03 +03:00
parent fb833ef01c
commit 3a52960d5b
9 changed files with 127 additions and 62 deletions
+78 -3
View File
@@ -1,6 +1,7 @@
package models
import (
"strings"
"sync"
)
@@ -17,7 +18,81 @@ var CurrentEnvironment = Environment{
var LogMessages bool
var LogFile string
var SyncQueues = struct {
// QueueStore — хранилище очередей: блокировка + map ключ→очередь + индекс tenant→очереди.
//
// Ключ очереди в Queues — "{accessKey}:{queueName}" (tenant-scoped изоляция).
// Индекс tenantQueues (accessKey → набор имён очередей) позволяет считать и
// перечислять очереди тенанта за O(число его очередей), без скана всех очередей сервиса.
//
// ВАЖНО: методы AddQueue/RemoveQueue/CountTenantQueues/TenantQueueNames НЕ берут
// блокировку — вызывать под уже захваченным SyncQueues.Lock()/RLock()
// (соответствует существующей дисциплине блокировок в коде).
type QueueStore struct {
sync.RWMutex
Queues map[string]*Queue
}{Queues: make(map[string]*Queue)}
Queues map[string]*Queue
tenantQueues map[string]map[string]struct{}
}
var SyncQueues = QueueStore{
Queues: make(map[string]*Queue),
tenantQueues: make(map[string]map[string]struct{}),
}
// splitQueueKey — разбирает tenant-scoped ключ "{accessKey}:{queueName}".
// Легаси-ключи без ":" (yaml-очереди) tenant-независимы → ok=false.
func splitQueueKey(key string) (accessKey, queueName string, ok bool) {
idx := strings.Index(key, ":")
if idx <= 0 || idx >= len(key)-1 {
return "", "", false
}
return key[:idx], key[idx+1:], true
}
// AddQueue — добавляет очередь и обновляет tenant-индекс.
// Вызывать под SyncQueues.Lock().
func (s *QueueStore) AddQueue(key string, q *Queue) {
s.Queues[key] = q
if ak, name, ok := splitQueueKey(key); ok {
if s.tenantQueues[ak] == nil {
s.tenantQueues[ak] = make(map[string]struct{})
}
s.tenantQueues[ak][name] = struct{}{}
}
}
// RemoveQueue — удаляет очередь и обновляет tenant-индекс.
// Вызывать под SyncQueues.Lock().
func (s *QueueStore) RemoveQueue(key string) {
delete(s.Queues, key)
if ak, name, ok := splitQueueKey(key); ok {
if set, exists := s.tenantQueues[ak]; exists {
delete(set, name)
if len(set) == 0 {
delete(s.tenantQueues, ak)
}
}
}
}
// CountTenantQueues — число очередей тенанта (по accessKey) за O(1).
// Вызывать под SyncQueues.RLock() или Lock().
func (s *QueueStore) CountTenantQueues(accessKey string) int {
return len(s.tenantQueues[accessKey])
}
// TenantQueueNames — копия списка имён очередей тенанта.
// Вызывать под SyncQueues.RLock() или Lock().
func (s *QueueStore) TenantQueueNames(accessKey string) []string {
set := s.tenantQueues[accessKey]
names := make([]string, 0, len(set))
for n := range set {
names = append(names, n)
}
return names
}
// ResetQueueIndex — очищает tenant-индекс (для ResetResources/тестов).
// Вызывать под SyncQueues.Lock().
func (s *QueueStore) ResetQueueIndex() {
s.tenantQueues = make(map[string]map[string]struct{})
}