0.2.79 - Slow api fixes
This commit is contained in:
@@ -40,6 +40,7 @@ import (
|
||||
"github.com/ponzischeme89/memby/server/internal/scheduler"
|
||||
"github.com/ponzischeme89/memby/server/internal/sonarr"
|
||||
"github.com/ponzischeme89/memby/server/internal/store"
|
||||
"github.com/ponzischeme89/memby/server/internal/timing"
|
||||
)
|
||||
|
||||
type Server struct {
|
||||
@@ -107,6 +108,19 @@ type Server struct {
|
||||
// playbackTitles lets a progress or stop report, which carries only an item id, be
|
||||
// logged by name.
|
||||
playbackTitles playbackTitles
|
||||
// featurePolicy keeps the operator's feature switches out of the request path. See
|
||||
// features_cache.go.
|
||||
featurePolicy featurePolicyCache
|
||||
// upstream deduplicates concurrent cache misses for the same key, so two televisions
|
||||
// asking for the same expensive answer at the same moment cost one upstream call
|
||||
// rather than two. See coalesce.go.
|
||||
upstream upstreamGroup
|
||||
// household caches the completion scores every viewer's ranking reads and none of
|
||||
// them can get a different answer to. See ranking.go.
|
||||
household householdScores
|
||||
// followChecks stops the automatic My Shows check repeating for the length of an
|
||||
// episode. See playback_follow.go.
|
||||
followChecks followChecks
|
||||
|
||||
// ingestRuns collapses a season pack's worth of finished scans into one banner.
|
||||
ingestRuns ingestRuns
|
||||
@@ -505,6 +519,13 @@ func (s *Server) withLogging(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
start := time.Now()
|
||||
r, identity := withRequestIdentity(r)
|
||||
// Every layer under this one — the Emby client, the *arr clients, Postgres,
|
||||
// Redis — records against this. It is installed for every request rather than
|
||||
// only for ones that turn out to be slow, because nothing knows a request is
|
||||
// slow until it has finished and the evidence has to have been collected on the
|
||||
// way through. What it costs when the request is fast is a map nobody reads.
|
||||
ctx, trace := timing.New(r.Context())
|
||||
r = r.WithContext(ctx)
|
||||
w.Header().Set("X-Memby-Correlation", identity.correlation)
|
||||
s.loggerFor(r.Context()).Log(r.Context(), serverlogging.LevelTrace, "request started",
|
||||
"method", r.Method, "path", r.URL.Path, "query_keys", queryKeys(r))
|
||||
@@ -539,6 +560,16 @@ func (s *Server) withLogging(next http.Handler) http.Handler {
|
||||
if cached := rec.Header().Get("X-Memby-Cache"); cached != "" {
|
||||
fields = append(fields, "cache", cached)
|
||||
}
|
||||
// A duration on its own says something is slow and nothing about why, which is
|
||||
// the state every latency investigation here started from. The breakdown is
|
||||
// attached only past the threshold: on a fast request it would be a second
|
||||
// column of noise on every line, and on a slow one it is the answer.
|
||||
if elapsed := time.Since(start); elapsed >= s.slowRequestThreshold() && !trace.Empty() {
|
||||
fields = append(fields, "breakdown", trace.Breakdown())
|
||||
if rest := trace.Unattributed(elapsed); rest > 0 {
|
||||
fields = append(fields, "elsewhere", rest.Round(time.Millisecond))
|
||||
}
|
||||
}
|
||||
s.log.Log(r.Context(), level, "request", fields...)
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user