[ARVADOS] updated: 2.1.0-1582-g36b4a526b
Git user
git at public.arvados.org
Fri Nov 5 19:08:23 UTC 2021
Summary of changes:
.gitignore | 1 +
lib/controller/federation/conn.go | 5 ++-
lib/controller/handler.go | 17 +++++---
lib/controller/localdb/conn.go | 88 +++++++++++++++++----------------------
4 files changed, 55 insertions(+), 56 deletions(-)
via 36b4a526be01a76eeb2ef2db645598f45f1de992 (commit)
from 3f32ceb98c74d4c1ad056615c3dac359ad0c0bce (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
commit 36b4a526be01a76eeb2ef2db645598f45f1de992
Author: Lucas Di Pentima <lucas.dipentima at curii.com>
Date: Fri Nov 5 16:07:37 2021 -0300
17944: Adds /_health/vocabulary health endpoint. Improves cache refreshing.
Arvados-DCO-1.1-Signed-off-by: Lucas Di Pentima <lucas.dipentima at curii.com>
diff --git a/.gitignore b/.gitignore
index beb84b3c2..231424acc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -32,5 +32,6 @@ services/api/config/arvados-clients.yml
.Rproj.user
_version.py
*.bak
+*.log
arvados-snakeoil-ca.pem
.vagrant
diff --git a/lib/controller/federation/conn.go b/lib/controller/federation/conn.go
index d47730352..7efbda8d1 100644
--- a/lib/controller/federation/conn.go
+++ b/lib/controller/federation/conn.go
@@ -22,6 +22,7 @@ import (
"git.arvados.org/arvados.git/sdk/go/arvados"
"git.arvados.org/arvados.git/sdk/go/auth"
"git.arvados.org/arvados.git/sdk/go/ctxlog"
+ "git.arvados.org/arvados.git/sdk/go/health"
)
type Conn struct {
@@ -30,7 +31,7 @@ type Conn struct {
remotes map[string]backend
}
-func New(cluster *arvados.Cluster) *Conn {
+func New(cluster *arvados.Cluster, vocHealthFunc *health.Func) *Conn {
local := localdb.NewConn(cluster)
remotes := map[string]backend{}
for id, remote := range cluster.RemoteClusters {
@@ -44,6 +45,8 @@ func New(cluster *arvados.Cluster) *Conn {
remotes[id] = conn
}
+ *vocHealthFunc = local.LastVocabularyError
+
return &Conn{
cluster: cluster,
local: local,
diff --git a/lib/controller/handler.go b/lib/controller/handler.go
index 358b0ed0c..22d2e8329 100644
--- a/lib/controller/handler.go
+++ b/lib/controller/handler.go
@@ -100,17 +100,22 @@ func neverRedirect(*http.Request, []*http.Request) error { return http.ErrUseLas
func (h *Handler) setup() {
mux := http.NewServeMux()
- mux.Handle("/_health/", &health.Handler{
- Token: h.Cluster.ManagementToken,
- Prefix: "/_health/",
- Routes: health.Routes{"ping": func() error { _, err := h.db(context.TODO()); return err }},
- })
+ var vocHealthFunc health.Func
oidcAuthorizer := localdb.OIDCAccessTokenAuthorizer(h.Cluster, h.db)
- rtr := router.New(federation.New(h.Cluster), router.Config{
+ rtr := router.New(federation.New(h.Cluster, &vocHealthFunc), router.Config{
MaxRequestSize: h.Cluster.API.MaxRequestSize,
WrapCalls: api.ComposeWrappers(ctrlctx.WrapCallsInTransactions(h.db), oidcAuthorizer.WrapCalls),
})
+
+ mux.Handle("/_health/", &health.Handler{
+ Token: h.Cluster.ManagementToken,
+ Prefix: "/_health/",
+ Routes: health.Routes{
+ "ping": func() error { _, err := h.db(context.TODO()); return err },
+ "vocabulary": vocHealthFunc,
+ },
+ })
mux.Handle("/arvados/v1/config", rtr)
mux.Handle("/arvados/v1/vocabulary", rtr)
mux.Handle("/"+arvados.EndpointUserAuthenticate.Path, rtr) // must come before .../users/
diff --git a/lib/controller/localdb/conn.go b/lib/controller/localdb/conn.go
index 9d1aa5362..f51567315 100644
--- a/lib/controller/localdb/conn.go
+++ b/lib/controller/localdb/conn.go
@@ -11,23 +11,24 @@ import (
"net/http"
"os"
"strings"
+ "time"
"git.arvados.org/arvados.git/lib/controller/railsproxy"
"git.arvados.org/arvados.git/lib/controller/rpc"
"git.arvados.org/arvados.git/sdk/go/arvados"
"git.arvados.org/arvados.git/sdk/go/ctxlog"
"git.arvados.org/arvados.git/sdk/go/httpserver"
- "github.com/fsnotify/fsnotify"
- "github.com/sirupsen/logrus"
)
type railsProxy = rpc.Conn
type Conn struct {
- cluster *arvados.Cluster
- *railsProxy // handles API methods that aren't defined on Conn itself
- vocabularyCache *arvados.Vocabulary
- reloadVocabulary bool
+ cluster *arvados.Cluster
+ *railsProxy // handles API methods that aren't defined on Conn itself
+ vocabularyCache *arvados.Vocabulary
+ vocabularyFileModTime time.Time
+ lastVocabularyRefreshCheck time.Time
+ lastVocabularyError error
loginController
}
@@ -69,43 +70,34 @@ func (conn *Conn) checkProperties(ctx context.Context, properties interface{}) e
return nil
}
-func watchVocabulary(logger logrus.FieldLogger, vocPath string, fn func()) {
- watcher, err := fsnotify.NewWatcher()
- if err != nil {
- logger.WithError(err).Error("vocabulary fsnotify setup failed")
- return
+func (conn *Conn) maybeRefreshVocabularyCache() error {
+ if conn.lastVocabularyRefreshCheck.Add(time.Second).After(time.Now()) {
+ // Throttle the access to disk to at most once per second.
+ return nil
}
- defer watcher.Close()
-
- err = watcher.Add(vocPath)
+ conn.lastVocabularyRefreshCheck = time.Now()
+ fi, err := os.Stat(conn.cluster.API.VocabularyPath)
if err != nil {
- logger.WithError(err).Error("vocabulary file watcher failed")
- return
+ err = fmt.Errorf("couldn't stat vocabulary file %q: %v", conn.cluster.API.VocabularyPath, err)
+ conn.lastVocabularyError = err
+ return err
}
-
- for {
- select {
- case err, ok := <-watcher.Errors:
- if !ok {
- return
- }
- logger.WithError(err).Warn("vocabulary file watcher error")
- case _, ok := <-watcher.Events:
- if !ok {
- return
- }
- for len(watcher.Events) > 0 {
- <-watcher.Events
- }
- fn()
+ if fi.ModTime().After(conn.vocabularyFileModTime) {
+ err = conn.loadVocabularyFile()
+ if err != nil {
+ conn.lastVocabularyError = err
+ return err
}
+ conn.vocabularyFileModTime = fi.ModTime()
+ conn.lastVocabularyError = nil
}
+ return nil
}
func (conn *Conn) loadVocabularyFile() error {
vf, err := os.ReadFile(conn.cluster.API.VocabularyPath)
if err != nil {
- return fmt.Errorf("couldn't read vocabulary file %q: %v", conn.cluster.API.VocabularyPath, err)
+ return fmt.Errorf("couldn't reading the vocabulary file: %v", err)
}
mk := make([]string, 0, len(conn.cluster.Collections.ManagedProperties))
for k := range conn.cluster.Collections.ManagedProperties {
@@ -123,6 +115,13 @@ func (conn *Conn) loadVocabularyFile() error {
return nil
}
+// LastVocabularyError returns the last error encountered while loading the
+// vocabulary file.
+func (conn *Conn) LastVocabularyError() error {
+ conn.maybeRefreshVocabularyCache()
+ return conn.lastVocabularyError
+}
+
// VocabularyGet refreshes the vocabulary cache if necessary and returns it.
func (conn *Conn) VocabularyGet(ctx context.Context) (arvados.Vocabulary, error) {
if conn.cluster.API.VocabularyPath == "" {
@@ -136,24 +135,15 @@ func (conn *Conn) VocabularyGet(ctx context.Context) (arvados.Vocabulary, error)
err := conn.loadVocabularyFile()
if err != nil {
logger.WithError(err).Error("error loading vocabulary file")
- return arvados.Vocabulary{
- Tags: map[string]arvados.VocabularyTag{},
- }, err
- }
- go watchVocabulary(logger, conn.cluster.API.VocabularyPath, func() {
- logger.Info("vocabulary file changed, it'll be reloaded next time it's needed")
- conn.reloadVocabulary = true
- })
- } else if conn.reloadVocabulary {
- // Requested reload of vocabulary file.
- conn.reloadVocabulary = false
- err := conn.loadVocabularyFile()
- if err != nil {
- logger.WithError(err).Error("error reloading vocabulary file - ignoring")
- } else {
- logger.Info("vocabulary file reloaded successfully")
+ return arvados.Vocabulary{}, err
}
}
+ err := conn.maybeRefreshVocabularyCache()
+ if err != nil {
+ logger.WithError(err).Error("error reloading vocabulary file - ignoring")
+ } else {
+ logger.Info("vocabulary file reloaded successfully")
+ }
return *conn.vocabularyCache, nil
}
-----------------------------------------------------------------------
hooks/post-receive
--
More information about the arvados-commits
mailing list