router/internal/circuit/breaker.go (46 lines of code) (raw):

package circuit import ( "context" "net/http" rcontext "github.com/wundergraph/cosmo/router/internal/context" "go.uber.org/zap" ) type Breaker struct { roundTripper http.RoundTripper loggerFunc func(req *http.Request) *zap.Logger circuitBreaker *Manager } func NewCircuitTripper(roundTripper http.RoundTripper, breaker *Manager, logger func(req *http.Request) *zap.Logger) *Breaker { return &Breaker{ circuitBreaker: breaker, loggerFunc: logger, roundTripper: roundTripper, } } func (rt *Breaker) RoundTrip(req *http.Request) (resp *http.Response, err error) { ctx := req.Context() var subgraph string subgraphCtxVal := ctx.Value(rcontext.CurrentSubgraphContextKey{}) if subgraphCtxVal != nil { if sg, ok := subgraphCtxVal.(string); ok { subgraph = sg } } // If there is no circuit defined for this subgraph circuit := rt.circuitBreaker.GetCircuitBreaker(subgraph) if circuit == nil { return rt.roundTripper.RoundTrip(req) } preRunStatus := circuit.IsOpen() err = circuit.Run(req.Context(), func(_ context.Context) error { resp, err = rt.roundTripper.RoundTrip(req) return err }) postRunStatus := circuit.IsOpen() logger := rt.loggerFunc(req) if preRunStatus != postRunStatus { logger.Debug("Circuit breaker status changed", zap.String("subgraph_name", subgraph), zap.Bool("is_open", postRunStatus)) } else if preRunStatus { logger.Debug("Circuit breaker open, request callback did not execute", zap.String("subgraph_name", subgraph)) } return resp, err }