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:
+78
-3
@@ -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{})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user