Files
configcenter/internal/runtime/memory/memory.go

121 lines
2.8 KiB
Go

package memory
import (
"context"
"encoding/json"
"sync"
"github.com/longpeng/configcenter/internal/domain"
runtimepkg "github.com/longpeng/configcenter/internal/runtime"
)
type value struct {
items map[string]string
revision int64
version int
}
type historyEvent struct {
key string
event domain.ConfigEvent
}
type subscriber struct {
key string
ch chan runtimepkg.WatchResult
}
type Store struct {
mu sync.RWMutex
revision int64
values map[string]value
subscribers map[int64]subscriber
nextSubID int64
history []historyEvent
}
func New() *Store {
return &Store{values: make(map[string]value), subscribers: make(map[int64]subscriber)}
}
func (s *Store) Close() error {
s.mu.Lock()
defer s.mu.Unlock()
for id, sub := range s.subscribers {
close(sub.ch)
delete(s.subscribers, id)
}
return nil
}
func (s *Store) Ping(context.Context) error { return nil }
func (s *Store) Put(_ context.Context, key string, payload []byte, release domain.Release) (int64, error) {
items := make(map[string]string)
if err := json.Unmarshal(payload, &items); err != nil {
return 0, err
}
s.mu.Lock()
s.revision++
revision := s.revision
s.values[key] = value{items: clone(items), revision: revision, version: release.Version}
event := domain.ConfigEvent{Type: "UPDATED", Items: clone(items), Revision: revision}
s.history = append(s.history, historyEvent{key: key, event: event})
if len(s.history) > 256 {
s.history = append([]historyEvent(nil), s.history[len(s.history)-256:]...)
}
for _, sub := range s.subscribers {
if sub.key != key {
continue
}
select {
case sub.ch <- runtimepkg.WatchResult{Event: event}:
default:
}
}
s.mu.Unlock()
return revision, nil
}
func (s *Store) Get(_ context.Context, key string) (domain.RuntimeConfig, error) {
s.mu.RLock()
defer s.mu.RUnlock()
current, ok := s.values[key]
if !ok {
return domain.RuntimeConfig{Items: map[string]string{}, Revision: s.revision}, nil
}
return domain.RuntimeConfig{Items: clone(current.items), Revision: current.revision, ReleaseVersion: current.version}, nil
}
func (s *Store) Watch(ctx context.Context, key string, startRevision int64) <-chan runtimepkg.WatchResult {
ch := make(chan runtimepkg.WatchResult, 16)
s.mu.Lock()
s.nextSubID++
id := s.nextSubID
s.subscribers[id] = subscriber{key: key, ch: ch}
for _, historical := range s.history {
if historical.key == key && historical.event.Revision >= startRevision && startRevision > 0 {
ch <- runtimepkg.WatchResult{Event: historical.event}
}
}
s.mu.Unlock()
go func() {
<-ctx.Done()
s.mu.Lock()
if _, ok := s.subscribers[id]; ok {
delete(s.subscribers, id)
close(ch)
}
s.mu.Unlock()
}()
return ch
}
func clone(input map[string]string) map[string]string {
result := make(map[string]string, len(input))
for key, item := range input {
result[key] = item
}
return result
}