Use queues for the pod events (#30)

This commit is contained in:
Murat Kabilov 2017-05-23 15:24:14 +02:00 committed by GitHub
parent 132c8425e6
commit 009db16c7c
6 changed files with 96 additions and 79 deletions

View File

@ -17,6 +17,7 @@ import (
"k8s.io/client-go/pkg/apis/apps/v1beta1" "k8s.io/client-go/pkg/apis/apps/v1beta1"
"k8s.io/client-go/pkg/types" "k8s.io/client-go/pkg/types"
"k8s.io/client-go/rest" "k8s.io/client-go/rest"
"k8s.io/client-go/tools/cache"
"github.com/zalando-incubator/postgres-operator/pkg/spec" "github.com/zalando-incubator/postgres-operator/pkg/spec"
"github.com/zalando-incubator/postgres-operator/pkg/util" "github.com/zalando-incubator/postgres-operator/pkg/util"
@ -54,18 +55,17 @@ type Cluster struct {
kubeResources kubeResources
spec.Postgresql spec.Postgresql
Config Config
logger *logrus.Entry logger *logrus.Entry
pgUsers map[string]spec.PgUser pgUsers map[string]spec.PgUser
systemUsers map[string]spec.PgUser systemUsers map[string]spec.PgUser
podEvents chan spec.PodEvent podSubscribers map[spec.NamespacedName]chan spec.PodEvent
podSubscribers map[spec.NamespacedName]chan spec.PodEvent podSubscribersMu sync.RWMutex
podSubscribersMu sync.RWMutex pgDb *sql.DB
pgDb *sql.DB mu sync.Mutex
mu sync.Mutex masterLess bool
masterLess bool userSyncStrategy spec.UserSyncer
podDispatcherRunning bool deleteOptions *v1.DeleteOptions
userSyncStrategy spec.UserSyncer podEventsQueue *cache.FIFO
deleteOptions *v1.DeleteOptions
} }
func New(cfg Config, pgSpec spec.Postgresql, logger *logrus.Entry) *Cluster { func New(cfg Config, pgSpec spec.Postgresql, logger *logrus.Entry) *Cluster {
@ -73,19 +73,27 @@ func New(cfg Config, pgSpec spec.Postgresql, logger *logrus.Entry) *Cluster {
kubeResources := kubeResources{Secrets: make(map[types.UID]*v1.Secret)} kubeResources := kubeResources{Secrets: make(map[types.UID]*v1.Secret)}
orphanDependents := true orphanDependents := true
podEventsQueue := cache.NewFIFO(func(obj interface{}) (string, error) {
e, ok := obj.(spec.PodEvent)
if !ok {
return "", fmt.Errorf("could not cast to PodEvent")
}
return fmt.Sprintf("%s-%s", e.PodName, e.ResourceVersion), nil
})
cluster := &Cluster{ cluster := &Cluster{
Config: cfg, Config: cfg,
Postgresql: pgSpec, Postgresql: pgSpec,
logger: lg, logger: lg,
pgUsers: make(map[string]spec.PgUser), pgUsers: make(map[string]spec.PgUser),
systemUsers: make(map[string]spec.PgUser), systemUsers: make(map[string]spec.PgUser),
podEvents: make(chan spec.PodEvent), podSubscribers: make(map[spec.NamespacedName]chan spec.PodEvent),
podSubscribers: make(map[spec.NamespacedName]chan spec.PodEvent), kubeResources: kubeResources,
kubeResources: kubeResources, masterLess: false,
masterLess: false, userSyncStrategy: users.DefaultUserSyncStrategy{},
podDispatcherRunning: false, deleteOptions: &v1.DeleteOptions{OrphanDependents: &orphanDependents},
userSyncStrategy: users.DefaultUserSyncStrategy{}, podEventsQueue: podEventsQueue,
deleteOptions: &v1.DeleteOptions{OrphanDependents: &orphanDependents},
} }
return cluster return cluster
@ -143,16 +151,11 @@ func (c *Cluster) initUsers() error {
return nil return nil
} }
func (c *Cluster) Create(stopCh <-chan struct{}) error { func (c *Cluster) Create() error {
c.mu.Lock() c.mu.Lock()
defer c.mu.Unlock() defer c.mu.Unlock()
var err error var err error
if !c.podDispatcherRunning {
go c.podEventsDispatcher(stopCh)
c.podDispatcherRunning = true
}
defer func() { defer func() {
if err == nil { if err == nil {
c.setStatus(spec.ClusterStatusRunning) //TODO: are you sure it's running? c.setStatus(spec.ClusterStatusRunning) //TODO: are you sure it's running?
@ -460,7 +463,38 @@ func (c *Cluster) Delete() error {
} }
func (c *Cluster) ReceivePodEvent(event spec.PodEvent) { func (c *Cluster) ReceivePodEvent(event spec.PodEvent) {
c.podEvents <- event c.podEventsQueue.Add(event)
}
func (c *Cluster) processPodEvent(obj interface{}) error {
event, ok := obj.(spec.PodEvent)
if !ok {
return fmt.Errorf("could not cast to PodEvent")
}
c.podSubscribersMu.RLock()
subscriber, ok := c.podSubscribers[event.PodName]
c.podSubscribersMu.RUnlock()
if ok {
subscriber <- event
}
return nil
}
func (c *Cluster) Run(stopCh <-chan struct{}) {
go c.processPodEventQueue(stopCh)
}
func (c *Cluster) processPodEventQueue(stopCh <-chan struct{}) {
for {
select {
case <-stopCh:
return
default:
c.podEventsQueue.Pop(cache.PopProcessFunc(c.processPodEvent))
}
}
} }
func (c *Cluster) initSystemUsers() { func (c *Cluster) initSystemUsers() {

View File

@ -146,23 +146,6 @@ func (c *Cluster) recreatePod(pod v1.Pod) error {
return nil return nil
} }
func (c *Cluster) podEventsDispatcher(stopCh <-chan struct{}) {
c.logger.Infof("Watching '%s' cluster", c.ClusterName())
for {
select {
case event := <-c.podEvents:
c.podSubscribersMu.RLock()
subscriber, ok := c.podSubscribers[event.PodName]
c.podSubscribersMu.RUnlock()
if ok {
go func() { subscriber <- event }() //TODO: is it a right way to do nonblocking send to the channel?
}
case <-stopCh:
return
}
}
}
func (c *Cluster) recreatePods() error { func (c *Cluster) recreatePods() error {
ls := c.labelsSet() ls := c.labelsSet()
namespace := c.Metadata.Namespace namespace := c.Metadata.Namespace

View File

@ -7,7 +7,7 @@ import (
"github.com/zalando-incubator/postgres-operator/pkg/util/k8sutil" "github.com/zalando-incubator/postgres-operator/pkg/util/k8sutil"
) )
func (c *Cluster) Sync(stopCh <-chan struct{}) error { func (c *Cluster) Sync() error {
c.mu.Lock() c.mu.Lock()
defer c.mu.Unlock() defer c.mu.Unlock()
@ -16,11 +16,6 @@ func (c *Cluster) Sync(stopCh <-chan struct{}) error {
c.logger.Errorf("could not load resources: %v", err) c.logger.Errorf("could not load resources: %v", err)
} }
if !c.podDispatcherRunning {
go c.podEventsDispatcher(stopCh)
c.podDispatcherRunning = true
}
c.logger.Debugf("Syncing secrets") c.logger.Debugf("Syncing secrets")
if err := c.syncSecrets(); err != nil { if err := c.syncSecrets(); err != nil {
if !k8sutil.ResourceAlreadyExists(err) { if !k8sutil.ResourceAlreadyExists(err) {

View File

@ -62,10 +62,11 @@ func (c *Controller) podAdd(obj interface{}) {
} }
podEvent := spec.PodEvent{ podEvent := spec.PodEvent{
ClusterName: c.PodClusterName(pod), ClusterName: c.PodClusterName(pod),
PodName: util.NameFromMeta(pod.ObjectMeta), PodName: util.NameFromMeta(pod.ObjectMeta),
CurPod: pod, CurPod: pod,
EventType: spec.EventAdd, EventType: spec.EventAdd,
ResourceVersion: pod.ResourceVersion,
} }
c.podCh <- podEvent c.podCh <- podEvent
@ -83,11 +84,12 @@ func (c *Controller) podUpdate(prev, cur interface{}) {
} }
podEvent := spec.PodEvent{ podEvent := spec.PodEvent{
ClusterName: c.PodClusterName(curPod), ClusterName: c.PodClusterName(curPod),
PodName: util.NameFromMeta(curPod.ObjectMeta), PodName: util.NameFromMeta(curPod.ObjectMeta),
PrevPod: prevPod, PrevPod: prevPod,
CurPod: curPod, CurPod: curPod,
EventType: spec.EventUpdate, EventType: spec.EventUpdate,
ResourceVersion: curPod.ResourceVersion,
} }
c.podCh <- podEvent c.podCh <- podEvent
@ -100,27 +102,28 @@ func (c *Controller) podDelete(obj interface{}) {
} }
podEvent := spec.PodEvent{ podEvent := spec.PodEvent{
ClusterName: c.PodClusterName(pod), ClusterName: c.PodClusterName(pod),
PodName: util.NameFromMeta(pod.ObjectMeta), PodName: util.NameFromMeta(pod.ObjectMeta),
CurPod: pod, CurPod: pod,
EventType: spec.EventDelete, EventType: spec.EventDelete,
ResourceVersion: pod.ResourceVersion,
} }
c.podCh <- podEvent c.podCh <- podEvent
} }
func (c *Controller) podEventsDispatcher(stopCh <-chan struct{}) { func (c *Controller) podEventsDispatcher(stopCh <-chan struct{}) {
c.logger.Infof("Watching all pod events") c.logger.Debugln("Watching all pod events")
for { for {
select { select {
case event := <-c.podCh: case event := <-c.podCh:
c.clustersMu.RLock() c.clustersMu.RLock()
subscriber, ok := c.clusters[event.ClusterName] cluster, ok := c.clusters[event.ClusterName]
c.clustersMu.RUnlock() c.clustersMu.RUnlock()
if ok { if ok {
c.logger.Debugf("Sending %s event of pod '%s' to the '%s' cluster channel", event.EventType, event.PodName, event.ClusterName) c.logger.Debugf("Sending %s event of pod '%s' to the '%s' cluster channel", event.EventType, event.PodName, event.ClusterName)
go subscriber.ReceivePodEvent(event) cluster.ReceivePodEvent(event)
} }
case <-stopCh: case <-stopCh:
return return

View File

@ -91,7 +91,6 @@ func (c *Controller) processEvent(obj interface{}) error {
c.clustersMu.RLock() c.clustersMu.RLock()
cl, clusterFound := c.clusters[clusterName] cl, clusterFound := c.clusters[clusterName]
stopCh := c.stopChs[clusterName]
c.clustersMu.RUnlock() c.clustersMu.RUnlock()
switch event.EventType { switch event.EventType {
@ -105,13 +104,14 @@ func (c *Controller) processEvent(obj interface{}) error {
stopCh := make(chan struct{}) stopCh := make(chan struct{})
cl = cluster.New(c.makeClusterConfig(), *event.NewSpec, logger) cl = cluster.New(c.makeClusterConfig(), *event.NewSpec, logger)
cl.Run(stopCh)
c.clustersMu.Lock() c.clustersMu.Lock()
c.clusters[clusterName] = cl c.clusters[clusterName] = cl
c.stopChs[clusterName] = stopCh c.stopChs[clusterName] = stopCh
c.clustersMu.Unlock() c.clustersMu.Unlock()
if err := cl.Create(stopCh); err != nil { if err := cl.Create(); err != nil {
cl.Error = fmt.Errorf("could not create cluster: %v", err) cl.Error = fmt.Errorf("could not create cluster: %v", err)
logger.Errorf("%v", cl.Error) logger.Errorf("%v", cl.Error)
@ -158,8 +158,9 @@ func (c *Controller) processEvent(obj interface{}) error {
// no race condition because a cluster is always processed by single worker // no race condition because a cluster is always processed by single worker
if !clusterFound { if !clusterFound {
stopCh := make(chan struct{})
cl = cluster.New(c.makeClusterConfig(), *event.NewSpec, logger) cl = cluster.New(c.makeClusterConfig(), *event.NewSpec, logger)
stopCh = make(chan struct{}) cl.Run(stopCh)
c.clustersMu.Lock() c.clustersMu.Lock()
c.clusters[clusterName] = cl c.clusters[clusterName] = cl
@ -167,7 +168,7 @@ func (c *Controller) processEvent(obj interface{}) error {
c.clustersMu.Unlock() c.clustersMu.Unlock()
} }
if err := cl.Sync(stopCh); err != nil { if err := cl.Sync(); err != nil {
cl.Error = fmt.Errorf("could not sync cluster '%s': %s", clusterName, err) cl.Error = fmt.Errorf("could not sync cluster '%s': %s", clusterName, err)
logger.Errorf("%v", cl) logger.Errorf("%v", cl)
return nil return nil

View File

@ -34,11 +34,12 @@ const (
) )
type PodEvent struct { type PodEvent struct {
ClusterName NamespacedName ResourceVersion string
PodName NamespacedName ClusterName NamespacedName
PrevPod *v1.Pod PodName NamespacedName
CurPod *v1.Pod PrevPod *v1.Pod
EventType EventType CurPod *v1.Pod
EventType EventType
} }
type PgUser struct { type PgUser struct {