Circuit breaker pattern

Radim Štěpaník

https://app.sli.do/event/heoixjpa/live/questions

slido.com:

 👀 CTO

👷 Teamleader
👩‍💻 JS developer

🎤 Moderátor

🎸 Kytarista
👨‍👧 Rodič

(snad lepší než kytarista)

 🎧 Listen on spotify 

🐦 Follow me on twitter

kinapets

QCAST

⚡️Jistič

Kdy nad tím přemýšlet

  • distribuované prostředí
  • fail fast
  • fault tolerance

Chyby jsou všude

  • síťové chyby
  • výpadky služeb
  • výpadky v datacentru
  • chyby v kódu

Typický scénář

Něco nám začíná hořet

Retry

  • Volání opakuj v jakémkoliv případě
  • Možné vylepšení rozprostřít volání v čase
  • Dobré dokud všechno funguje

💥

👉

Jak to asi dopadne?

Kompletní pád

Co dělá CB?

Rozpojeno

Čekáme na zotavení

Jistič se automaticky sepne

Circuit breaker

  • Stav jističe
    • chyby, timeouty
  • Samotná operace
  • Fallback
  • Nastavení 
    • práh erroru
    • timeout
    • .... ochranné pásmo - čas,
      množství, etc.

Podpora

  • vlastní implementace
  • knihovny
    • nodejs - https://www.npmjs.com/package/opossum
    • resilience4j - https://github.com/resilience4j/resilience4j
    • php - https://github.com/upwork/phystrix
    • ....

Code example

  • Stav jističe
    • chyby, timeouty
  • Samotná operace
  • Fallback
  • Nastavení 
    • práh erroru
    • timeout
    • .... ochranné pásmo - čas,
      množství, etc.
import CircuitBreaker from 'opossum';

function asyncFunctionThatCouldFail(x, y) {
    return new Promise((resolve, reject) => {
        // Do something, maybe on the network or a disk
    });
}

const options = {
    timeout: 3000, // If our function takes longer than 3 seconds, trigger a failure
    errorThresholdPercentage: 50, // When 50% of requests fail, trip the circuit
    resetTimeout: 30000, // After 30 seconds, try again.
};
const breaker = new CircuitBreaker(asyncFunctionThatCouldFail, options);

breaker.fire(x, y).then(console.log).catch(console.error);

Code example

  • Stav jističe
    • chyby, timeouty
  • Samotná operace
  • Fallback
  • Nastavení 
    • práh erroru
    • timeout
    • .... ochranné pásmo - čas,
      množství, etc.
import CircuitBreaker from 'opossum';

function asyncFunctionThatCouldFail(x, y) {
    return new Promise((resolve, reject) => {
        // Do something, maybe on the network or a disk
    });
}

const options = {
    timeout: 3000, // If our function takes longer than 3 seconds, trigger a failure
    errorThresholdPercentage: 50, // When 50% of requests fail, trip the circuit
    resetTimeout: 30000, // After 30 seconds, try again.
};
const breaker = new CircuitBreaker(asyncFunctionThatCouldFail, options)
    .fallback(() => 'do something else');    

breaker.fire(x, y).then(console.log).catch(console.error);

Use cases

  • IO operace 
    • http klienti
    • databazove operace - např. redis
    • čtení ze sdíleného souboru
  • High level logic
    • nastavení fallbacks
    • 💡Co takhle použití s graphql?

Věci na které nelze spoléhat

Use case

Products

Categories

Banners

GQL GW

50+ service

search

customer

cart

bestsellers

banners

menu

category

products

filters

content

    # Calls category
    getCategory(categoryUrl: $categoryUrl) {
      id
      howto
      # Calls content service
      ... on ContentCategory {
        content {
          id
          title
          body
        }
      }
      # Calls product service
      ... on ProductCategory {
        productCollection {
          items {
            ... on Product {
              ...productForList
            }
            # Calls estimated delivery service
            ... on BonusSet {
              estimatedDeliveries {
                ...productEstimatedDeliveryFragment
              }
            }
            # Calls banners service
            ... on SectionBannerSlideImage {
              ...sectionBannerSlide
            }
          }
        }
      }
    }
  
// 👉 every resolver has unique name - Query:category, Category:productCollection
const resolvers = {
    Query: {
        category: resolveCategory,
    },
    Category: {
        productCollection: resolveProductCollection,
    },
};

// 🌍 global configuration of specific resolvers
export const resolverConfig: Map<string, ResolverConfig> = new Map<string, ResolverConfig>()
    .set('Query:category', { timeout: 3000 })
    .set('Category:productCollection', { timeout: 3000, fallback: () => [] });

// 👷 Usage of high order function. Resolver is wrapped by circuit breaker
const resolversWithCircuitBreakers = mapResolvers(
    resolvers,
    // 
    ({ resolver, name }: CreateResolverInput): IFieldResolver<unknown, unknown, unknown> => {
        const { ...breakerDefaultConfig } = resolverConfig.get(name) ?? {};
        const config = { name, ...breakerDefaultConfig };
        const breaker = createCircuitBreaker(config, resolver);

        return async (...params: ResolverParams): Promise<unknown> => breaker.fire(...params);
    },
);

Nedokážeme to vyřešit obecně❓

Monitoring hystrix

Monitoring prometheus/grafana

  • sms notification
  • teams notification

Je odpovědí na všechno?

  • zvyšuje komplexitu aplikace
    • nutné správné nastavení timeoutů
    • nutné rozlišovat 
    • při použití přímo v kódu
  • zvyšuje nároky na zpracování 

👋 Díky za pozornost
Dotazy?

slido.com

Qeetup Circuit breaker

By Radim Štěpaník

Qeetup Circuit breaker

  • 290