Git Product home page Git Product logo

Comments (7)

danielfm avatar danielfm commented on July 17, 2024

Thanks for reporting this.

This is major problem for usage within web app servers, where you may have many processes coming in and out of existence at any time due to scaling behavior or processes sporadically dying and being resurrected.

As far as I understand - please @phillbaker correct me if I'm wrong - the Redis storage is intended to be used whenever you want to share state between breaker instances, making those act like a single unit due to the shared state between them. So, unless you create different CircuitRedisStorage (and providing different values for the namespace argument), this is expected behavior.

Maybe for the mentioned use case (circuit breakers within web servers, each breaker with its own state), you should use the default in-memory storage.

from pybreaker.

phillbaker avatar phillbaker commented on July 17, 2024

Hm, I think some more information would help. If the following is happening, then this does seem like a bug:

this will cause the fail counter to be reset anytime a new CircuitBreaker is instantiated using the same shared storage (like the provided CircuitRedisStorage).

I would have thought these two lines prevented overriding the shared counter and state in redis:

pybreaker/src/pybreaker.py

Lines 450 to 451 in c84055a

self._redis.setnx(self._namespace('fail_counter'), 0)
self._redis.setnx(self._namespace('state'), str(state))

@jcwilson - what impacts of this are you seeing in your environment?

from pybreaker.

phillbaker avatar phillbaker commented on July 17, 2024

Sorry for the double post, but I just saw #25. Would that also help address this issue?

from pybreaker.

alukach avatar alukach commented on July 17, 2024

@jcwilson Thanks for posting this, I am just getting started with pybreaker and ran into the same issue that you described.

@danielfm

the Redis storage is intended to be used whenever you want to share state between breaker instances, making those act like a single unit due to the shared state between them. So, unless you create different CircuitRedisStorage (and providing different values for the namespace argument), this is expected behavior.

Are you suggesting that the way that pybreaker is currently written is correct? My expectation would be that the library wouldn't reset the counter when you spin up a new instance of the breaker.

Maybe for the mentioned use case (circuit breakers within web servers, each breaker with its own state), you should use the default in-memory storage.

I think this is the opposite of the described use case. @jcwilson seems to be suggesting that he wants to share state between web servers but this is not really possible as each circuit breaker resets the global state of the breaker.

@phillbaker

I would have thought these two lines prevented overriding the shared counter and state in redis:

Those lines are indeed written to respect pre-existing state, however it's trampled over when initiating a circuit breaker due to the line posted in the issue's body:

class CircuitBreaker(object):
"""
More abstractly, circuit breakers exists to allow one subsystem to fail
without destroying the entire system.
This is done by wrapping dangerous operations (typically integration points)
with a component that can circumvent calls when the system is not healthy.
This pattern is described by Michael T. Nygard in his book 'Release It!'.
"""
def __init__(self, fail_max=5, reset_timeout=60, exclude=None,
listeners=None, state_storage=None):
"""
Creates a new circuit breaker with the given parameters.
"""
self._lock = threading.RLock()
if not state_storage:
self._state_storage = CircuitMemoryStorage(STATE_CLOSED)
else:
self._state_storage = state_storage
self._state = CircuitClosedState(self)

pybreaker/src/pybreaker.py

Lines 713 to 728 in c84055a

class CircuitClosedState(CircuitBreakerState):
"""
In the normal "closed" state, the circuit breaker executes operations as
usual. If the call succeeds, nothing happens. If it fails, however, the
circuit breaker makes a note of the failure.
Once the number of failures exceeds a threshold, the circuit breaker trips
and "opens" the circuit.
"""
def __init__(self, cb, prev_state=None, notify=False):
"""
Moves the given circuit breaker `cb` to the "closed" state.
"""
super(CircuitClosedState, self).__init__(cb, STATE_CLOSED)
self._breaker._state_storage.reset_counter()

from pybreaker.

alukach avatar alukach commented on July 17, 2024

Please see #27 for my suggestion on how to resolve this issue. Comments/critique welcome!

from pybreaker.

jcwilson avatar jcwilson commented on July 17, 2024

what impacts of this are you seeing in your environment?

Thanks a lot for taking a look. I neglected to mention that we started on version ~0.3.1 and have written our own redis backend that stores things in a slightly different manner, so it doesn't make use of the setnx() code in CircuitRedisStorage. That being said, I believe the current implementation as shipped with this library will still produce the same undesirable behavior. I believe the issue is best resolved at the CircuitBreaker level, rather than at the CircuitBreakerStorage level.

I think @alukach has expressed the problem pretty well. To keep things simple, let's speak in terms of a single circuit breaker instantiation in the code, but the code might be run in several processes (ie. uwsgi worker processes). We're using redis storage to provide a central shared counter for each circuit breaker's context so that an error on any box/process will increment the counter for all the rest, too. All of our processes will use the same namespace. I think if we were going to assign each process its own namespace, we'd just stick with the in-memory storage option and forego the overhead of the redis storage.

from pybreaker.

danielfm avatar danielfm commented on July 17, 2024

Closed by #27, feel free to re-open this if you have problems.

from pybreaker.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.