121 lines
2.8 KiB
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
|
|
}
|