func (p *policyd) Update(ctx context.Context) error {
	jobID := fastime.Now().Unix()
	glg.Infof("[%d] will update policy", jobID)
	eg := errgroup.Group{}
	rp := gache.New()
	for _, fetcher := range p.fetchers {
		f := fetcher  
		select {
		case <-ctx.Done():
			glg.Info("Update policy interrupted")
			return ctx.Err()
		default:
			eg.Go(func() error {
				select {
				case <-ctx.Done():
					glg.Info("Update policy interrupted")
					return ctx.Err()
				default:
					return fetchAndCachePolicy(ctx, rp, f)
				}
			})
		}
	}
	if err := eg.Wait(); err != nil {
		glg.Errorf("[%d] update policy fail", jobID)
		return err
	}
	rp.StartExpired(ctx, p.purgePeriod).
		EnableExpiredHook().
		SetExpiredHook(func(ctx context.Context, key string) {
			fetchAndCachePolicy(ctx, p.rolePolicies, p.fetchers[strings.Split(key, ":role.")[0]])
		})
	p.rolePolicies, rp = rp, p.rolePolicies
	glg.Debugf("tmp cache becomes effective")
	rp.Stop()
	rp.Clear()
	glg.Infof("[%d] update policy done", jobID)
	return nil
}
Update updates and cache policy data
package policy
import (
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"strings"
	"sync"
	"time"
	"github.com/AthenZ/athenz/utils/zpe-updater/util"
	"github.com/kpango/fastime"
	"github.com/kpango/gache"
	"github.com/kpango/glg"
	"github.com/pkg/errors"
	"github.com/yahoojapan/athenz-authorizer/v5/pubkey"
	"golang.org/x/sync/errgroup"
)
type Daemon interface {
	Start(context.Context) <-chan error
	Update(context.Context) error
	CheckPolicy(ctx context.Context, domain string, roles []string, action, resource string) error
	CheckPolicyRoles(ctx context.Context, domain string, roles []string, action, resource string) ([]string, error)
	GetPolicyCache(context.Context) map[string]interface{}
}
type roleEffect struct {
	Role   string
	Effect error
}
type policyd struct {
	rolePolicies gache.Gache
	expiryMargin  time.Duration  
	refreshPeriod time.Duration
	purgePeriod   time.Duration
	retryDelay    time.Duration
	retryAttempts int
	athenzURL     string
	athenzDomains []string
	client   *http.Client
	pkp      pubkey.Provider
	fetchers map[string]Fetcher  
}
func New(opts ...Option) (Daemon, error) {
	p := &policyd{
		rolePolicies: gache.New(),
	}
	for _, opt := range append(defaultOptions, opts...) {
		if err := opt(p); err != nil {
			return nil, errors.Wrap(err, "error create policyd")
		}
	}
	p.fetchers = make(map[string]Fetcher, len(p.athenzDomains))
	for _, domain := range p.athenzDomains {
		f := fetcher{
			domain:        domain,
			expiryMargin:  p.expiryMargin,
			retryDelay:    p.retryDelay,
			retryAttempts: p.retryAttempts,
			athenzURL:     p.athenzURL,
			spVerifier: func(sp *SignedPolicy) error {
				return sp.Verify(p.pkp)
			},
			client: p.client,
		}
		p.fetchers[domain] = &f
	}
	return p, nil
}
func (p *policyd) Start(ctx context.Context) <-chan error {
	glg.Info("Starting policyd updater")
	ech := make(chan error, 100)
	fch := make(chan struct{}, 1)
	go func() {
		defer close(fch)
		defer close(ech)
		ticker := time.NewTicker(p.refreshPeriod)
		for {
			select {
			case <-ctx.Done():
				glg.Info("Stopping policyd updater")
				ticker.Stop()
				ech <- ctx.Err()
				return
			case <-fch:
				if err := p.Update(ctx); err != nil {
					ech <- errors.Wrap(err, "error update policy")
					time.Sleep(p.retryDelay)
					select {
					case fch <- struct{}{}:
					default:
						glg.Warn("failure queue already full")
					}
				}
			case <-ticker.C:
				if err := p.Update(ctx); err != nil {
					ech <- errors.Wrap(err, "error update policy")
					select {
					case fch <- struct{}{}:
					default:
						glg.Warn("failure queue already full")
					}
				}
			}
		}
	}()
	return ech
}