resilient-circuit 0.4.1__py3-none-any.whl → 0.4.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- resilient_circuit/circuit_breaker.py +58 -15
- resilient_circuit/cli.py +3 -4
- resilient_circuit/retry.py +1 -1
- resilient_circuit/storage.py +73 -32
- {resilient_circuit-0.4.1.dist-info → resilient_circuit-0.4.2.dist-info}/METADATA +2 -3
- {resilient_circuit-0.4.1.dist-info → resilient_circuit-0.4.2.dist-info}/RECORD +10 -10
- {resilient_circuit-0.4.1.dist-info → resilient_circuit-0.4.2.dist-info}/WHEEL +0 -0
- {resilient_circuit-0.4.1.dist-info → resilient_circuit-0.4.2.dist-info}/entry_points.txt +0 -0
- {resilient_circuit-0.4.1.dist-info → resilient_circuit-0.4.2.dist-info}/licenses/LICENSE +0 -0
- {resilient_circuit-0.4.1.dist-info → resilient_circuit-0.4.2.dist-info}/top_level.txt +0 -0
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import abc
|
|
2
2
|
import enum
|
|
3
3
|
import logging
|
|
4
|
-
|
|
4
|
+
import time
|
|
5
|
+
from datetime import timedelta
|
|
5
6
|
from fractions import Fraction
|
|
6
7
|
from functools import wraps
|
|
7
8
|
from typing import Callable, Optional, TypeVar
|
|
@@ -11,7 +12,10 @@ from typing_extensions import ParamSpec
|
|
|
11
12
|
from resilient_circuit.buffer import BinaryCircularBuffer
|
|
12
13
|
from resilient_circuit.exceptions import ProtectedCallError
|
|
13
14
|
from resilient_circuit.policy import ProtectionPolicy
|
|
14
|
-
from resilient_circuit.storage import
|
|
15
|
+
from resilient_circuit.storage import (
|
|
16
|
+
CircuitBreakerStorage,
|
|
17
|
+
create_storage,
|
|
18
|
+
)
|
|
15
19
|
|
|
16
20
|
R = TypeVar("R")
|
|
17
21
|
P = ParamSpec("P")
|
|
@@ -56,7 +60,7 @@ class CircuitProtectorPolicy(ProtectionPolicy):
|
|
|
56
60
|
self._load_state()
|
|
57
61
|
|
|
58
62
|
def _load_state(self) -> None:
|
|
59
|
-
"""Load circuit breaker state from storage."""
|
|
63
|
+
"""Load circuit breaker state from storage including execution log buffer."""
|
|
60
64
|
try:
|
|
61
65
|
state_data = self.storage.get_state(self.resource_key)
|
|
62
66
|
if state_data:
|
|
@@ -64,6 +68,7 @@ class CircuitProtectorPolicy(ProtectionPolicy):
|
|
|
64
68
|
state = CircuitStatus(state_data["state"])
|
|
65
69
|
failure_count = int(state_data.get("failure_count", 0))
|
|
66
70
|
open_until = float(state_data.get("open_until", 0))
|
|
71
|
+
execution_log_data = state_data.get("execution_log")
|
|
67
72
|
|
|
68
73
|
# Initialize status based on stored state
|
|
69
74
|
status: CircuitStatusBase
|
|
@@ -77,6 +82,13 @@ class CircuitProtectorPolicy(ProtectionPolicy):
|
|
|
77
82
|
else: # HALF_OPEN
|
|
78
83
|
status = StatusHalfOpen(policy=self, failure_count=failure_count)
|
|
79
84
|
|
|
85
|
+
# Restore execution_log buffer if available
|
|
86
|
+
if execution_log_data and isinstance(execution_log_data, list):
|
|
87
|
+
if hasattr(status, 'execution_log') and hasattr(status.execution_log, '_items'):
|
|
88
|
+
# Restore buffer maintaining size limit
|
|
89
|
+
status.execution_log._items = execution_log_data[-status.execution_log.size:]
|
|
90
|
+
logger.debug(f"Restored buffer for {self.resource_key}: {len(status.execution_log._items)} entries")
|
|
91
|
+
|
|
80
92
|
self._status = status
|
|
81
93
|
logger.debug(f"Loaded circuit breaker state for {self.resource_key}: {state.value}")
|
|
82
94
|
else:
|
|
@@ -89,7 +101,7 @@ class CircuitProtectorPolicy(ProtectionPolicy):
|
|
|
89
101
|
self._status = StatusClosed(policy=self)
|
|
90
102
|
|
|
91
103
|
def _save_state(self) -> None:
|
|
92
|
-
"""Save circuit breaker state to storage."""
|
|
104
|
+
"""Save circuit breaker state to storage including execution log buffer."""
|
|
93
105
|
try:
|
|
94
106
|
state_value: str = self._status.status_type.value
|
|
95
107
|
failure_count_val: int = int(getattr(self._status, 'failure_count', 0))
|
|
@@ -99,13 +111,19 @@ class CircuitProtectorPolicy(ProtectionPolicy):
|
|
|
99
111
|
if hasattr(self._status, 'open_until_timestamp') else 0
|
|
100
112
|
)
|
|
101
113
|
|
|
114
|
+
# Extract execution_log buffer if available
|
|
115
|
+
execution_log_data = None
|
|
116
|
+
if hasattr(self._status, 'execution_log') and hasattr(self._status.execution_log, '_items'):
|
|
117
|
+
execution_log_data = list(self._status.execution_log._items)
|
|
118
|
+
|
|
102
119
|
self.storage.set_state(
|
|
103
120
|
self.resource_key,
|
|
104
121
|
state_value,
|
|
105
122
|
failure_count_val,
|
|
106
|
-
open_until_val
|
|
123
|
+
open_until_val,
|
|
124
|
+
execution_log=execution_log_data
|
|
107
125
|
)
|
|
108
|
-
logger.debug(f"Saved circuit breaker state for {self.resource_key}: {state_value}")
|
|
126
|
+
logger.debug(f"Saved circuit breaker state for {self.resource_key}: {state_value}, buffer_size={len(execution_log_data) if execution_log_data else 0}")
|
|
109
127
|
except Exception as e:
|
|
110
128
|
logger.error(f"Failed to save state for {self.resource_key}: {e}")
|
|
111
129
|
|
|
@@ -126,10 +144,8 @@ class CircuitProtectorPolicy(ProtectionPolicy):
|
|
|
126
144
|
new_status_obj = StatusClosed(policy=self, failure_count=0)
|
|
127
145
|
elif new_status is CircuitStatus.OPEN:
|
|
128
146
|
# When transitioning to OPEN, keep the failure count from current status
|
|
129
|
-
current_failure_count = getattr(self._status, 'failure_count', 0)
|
|
130
147
|
# Calculate the open_until timestamp based on current time and cooldown
|
|
131
|
-
|
|
132
|
-
open_until = (datetime.now() + self.cooldown).timestamp()
|
|
148
|
+
open_until = time.time() + self.cooldown.total_seconds()
|
|
133
149
|
new_status_obj = StatusOpen(policy=self, previous_status=self._status, open_until=open_until)
|
|
134
150
|
else: # HALF_OPEN
|
|
135
151
|
# When transitioning to HALF_OPEN, reset failure count
|
|
@@ -147,17 +163,28 @@ class CircuitProtectorPolicy(ProtectionPolicy):
|
|
|
147
163
|
def __call__(self, func: Callable[P, R]) -> Callable[P, R]:
|
|
148
164
|
@wraps(func)
|
|
149
165
|
def decorated(*args: P.args, **kwargs: P.kwargs) -> R:
|
|
166
|
+
import logging
|
|
167
|
+
logger = logging.getLogger(__name__)
|
|
168
|
+
|
|
150
169
|
self._status.validate_execution()
|
|
151
170
|
try:
|
|
152
171
|
result = func(*args, **kwargs)
|
|
153
172
|
except Exception as e:
|
|
154
|
-
|
|
173
|
+
should_fail = self.should_consider_failure(e)
|
|
174
|
+
logger.warning(
|
|
175
|
+
f"🚨 CB {self.resource_key}: Exception {type(e).__name__}: {e}, "
|
|
176
|
+
f"should_consider_failure={should_fail}"
|
|
177
|
+
)
|
|
178
|
+
if should_fail:
|
|
179
|
+
logger.warning(f"❌ CB {self.resource_key}: Calling mark_failure()")
|
|
155
180
|
self._status.mark_failure()
|
|
156
181
|
else:
|
|
182
|
+
logger.warning(f"✅ CB {self.resource_key}: Exception ignored, calling mark_success()")
|
|
157
183
|
self._status.mark_success()
|
|
158
184
|
self._save_state() # Persist state after exception
|
|
159
185
|
raise
|
|
160
186
|
else:
|
|
187
|
+
logger.warning(f"✅ CB {self.resource_key}: Success, calling mark_success()")
|
|
161
188
|
self._status.mark_success()
|
|
162
189
|
self._save_state() # Persist state after success
|
|
163
190
|
return result
|
|
@@ -205,13 +232,31 @@ class StatusClosed(CircuitStatusBase):
|
|
|
205
232
|
pass
|
|
206
233
|
|
|
207
234
|
def mark_failure(self) -> None:
|
|
235
|
+
import logging
|
|
236
|
+
logger = logging.getLogger(__name__)
|
|
237
|
+
|
|
208
238
|
self.failure_count += 1 # Increment failure count
|
|
209
239
|
self.execution_log.add(False)
|
|
240
|
+
|
|
241
|
+
logger.warning(
|
|
242
|
+
f"📊 CB {self.policy.resource_key} mark_failure: "
|
|
243
|
+
f"buffer={list(self.execution_log._items)}, "
|
|
244
|
+
f"is_full={self.execution_log.is_full}, "
|
|
245
|
+
f"size={self.execution_log.size}, "
|
|
246
|
+
f"failure_rate={self.execution_log.failure_rate}, "
|
|
247
|
+
f"threshold={self.policy.failure_limit}"
|
|
248
|
+
)
|
|
249
|
+
|
|
210
250
|
if (
|
|
211
251
|
self.execution_log.is_full
|
|
212
252
|
and self.execution_log.failure_rate >= self.policy.failure_limit
|
|
213
253
|
):
|
|
214
|
-
self.policy.
|
|
254
|
+
logger.warning(f"🔴 CB {self.policy.resource_key}: Opening circuit!")
|
|
255
|
+
try:
|
|
256
|
+
self.policy.status = CircuitStatus.OPEN
|
|
257
|
+
logger.warning(f"✅ CB {self.policy.resource_key}: Successfully set status to OPEN")
|
|
258
|
+
except Exception as e:
|
|
259
|
+
logger.error(f"❌ CB {self.policy.resource_key}: Failed to set status to OPEN: {type(e).__name__}: {e}", exc_info=True)
|
|
215
260
|
|
|
216
261
|
def mark_success(self) -> None:
|
|
217
262
|
self.failure_count = 0 # Reset failure count on success
|
|
@@ -232,17 +277,15 @@ class StatusOpen(CircuitStatusBase):
|
|
|
232
277
|
|
|
233
278
|
# Store the timestamp when the OPEN state should end (cooldown period)
|
|
234
279
|
# If open_until is 0, circuit should be blocked for the full cooldown period
|
|
235
|
-
from datetime import datetime
|
|
236
280
|
if open_until and open_until > 0:
|
|
237
281
|
self.open_until_timestamp = open_until # This is when cooldown ends
|
|
238
282
|
else:
|
|
239
283
|
# Calculate when cooldown should end based on current time and policy cooldown
|
|
240
|
-
self.open_until_timestamp =
|
|
284
|
+
self.open_until_timestamp = time.time() + policy.cooldown.total_seconds()
|
|
241
285
|
|
|
242
286
|
def validate_execution(self) -> None:
|
|
243
|
-
from datetime import datetime
|
|
244
287
|
# Check if cooldown period has expired
|
|
245
|
-
if
|
|
288
|
+
if time.time() >= self.open_until_timestamp:
|
|
246
289
|
# Cooldown expired, transition to HALF_OPEN to allow test requests
|
|
247
290
|
self.policy.status = CircuitStatus.HALF_OPEN
|
|
248
291
|
return # Allow execution in HALF_OPEN state
|
resilient_circuit/cli.py
CHANGED
|
@@ -5,7 +5,6 @@ CLI module for Highway Circuit Breaker
|
|
|
5
5
|
import argparse
|
|
6
6
|
import os
|
|
7
7
|
import sys
|
|
8
|
-
from typing import Optional
|
|
9
8
|
|
|
10
9
|
try:
|
|
11
10
|
import psycopg
|
|
@@ -66,7 +65,7 @@ def create_postgres_table(config: dict) -> bool:
|
|
|
66
65
|
|
|
67
66
|
table_exists = cur.fetchone()[0]
|
|
68
67
|
if table_exists:
|
|
69
|
-
print(
|
|
68
|
+
print("ℹ️ Table 'rc_circuit_breakers' already exists, checking for updates...")
|
|
70
69
|
|
|
71
70
|
# Create the circuit breaker table
|
|
72
71
|
cur.execute("""
|
|
@@ -180,7 +179,7 @@ def run_pg_setup(args: argparse.Namespace) -> int:
|
|
|
180
179
|
# Get config from environment
|
|
181
180
|
config = get_db_config_from_env()
|
|
182
181
|
|
|
183
|
-
print(
|
|
182
|
+
print("🔧 Using database configuration from environment:")
|
|
184
183
|
print(f" Host: {config['host']}")
|
|
185
184
|
print(f" Port: {config['port']}")
|
|
186
185
|
print(f" Database: {config['dbname']}")
|
|
@@ -258,4 +257,4 @@ def main():
|
|
|
258
257
|
return 1
|
|
259
258
|
|
|
260
259
|
if __name__ == '__main__':
|
|
261
|
-
sys.exit(main())
|
|
260
|
+
sys.exit(main())
|
resilient_circuit/retry.py
CHANGED
|
@@ -5,7 +5,7 @@ from typing import Callable, Optional, TypeVar
|
|
|
5
5
|
from typing_extensions import ParamSpec
|
|
6
6
|
|
|
7
7
|
from resilient_circuit.backoff import ExponentialDelay
|
|
8
|
-
from resilient_circuit.exceptions import
|
|
8
|
+
from resilient_circuit.exceptions import RetryLimitReached
|
|
9
9
|
from resilient_circuit.policy import ProtectionPolicy
|
|
10
10
|
|
|
11
11
|
R = TypeVar("R")
|
resilient_circuit/storage.py
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
+
import logging
|
|
1
2
|
import os
|
|
2
3
|
import time
|
|
3
4
|
from abc import ABC, abstractmethod
|
|
4
|
-
from typing import
|
|
5
|
-
import logging
|
|
5
|
+
from typing import Any, Dict, Optional
|
|
6
6
|
|
|
7
7
|
try:
|
|
8
8
|
from dotenv import load_dotenv
|
|
@@ -23,20 +23,24 @@ logger = logging.getLogger(__name__)
|
|
|
23
23
|
|
|
24
24
|
class CircuitBreakerStorage(ABC):
|
|
25
25
|
"""Abstract base class for circuit breaker storage backends."""
|
|
26
|
-
|
|
26
|
+
|
|
27
27
|
@abstractmethod
|
|
28
28
|
def get_state(self, resource_key: str) -> Optional[Dict[str, Any]]:
|
|
29
29
|
"""Get the state for a given resource key.
|
|
30
|
-
|
|
30
|
+
|
|
31
31
|
Returns:
|
|
32
|
-
Dictionary with keys: state, failure_count, open_until
|
|
32
|
+
Dictionary with keys: state, failure_count, open_until, execution_log (optional)
|
|
33
33
|
or None if no state found
|
|
34
34
|
"""
|
|
35
35
|
pass
|
|
36
|
-
|
|
36
|
+
|
|
37
37
|
@abstractmethod
|
|
38
|
-
def set_state(self, resource_key: str, state: str, failure_count: int, open_until: float) -> None:
|
|
39
|
-
"""Set the state for a given resource key.
|
|
38
|
+
def set_state(self, resource_key: str, state: str, failure_count: int, open_until: float, execution_log: Optional[list] = None) -> None:
|
|
39
|
+
"""Set the state for a given resource key.
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
execution_log: Optional list of boolean success/failure results
|
|
43
|
+
"""
|
|
40
44
|
pass
|
|
41
45
|
|
|
42
46
|
|
|
@@ -49,12 +53,15 @@ class InMemoryStorage(CircuitBreakerStorage):
|
|
|
49
53
|
def get_state(self, resource_key: str) -> Optional[Dict[str, Any]]:
|
|
50
54
|
return self._states.get(resource_key)
|
|
51
55
|
|
|
52
|
-
def set_state(self, resource_key: str, state: str, failure_count: int, open_until: float) -> None:
|
|
53
|
-
|
|
56
|
+
def set_state(self, resource_key: str, state: str, failure_count: int, open_until: float, execution_log: Optional[list] = None) -> None:
|
|
57
|
+
state_dict = {
|
|
54
58
|
"state": state,
|
|
55
59
|
"failure_count": failure_count,
|
|
56
60
|
"open_until": open_until
|
|
57
61
|
}
|
|
62
|
+
if execution_log is not None:
|
|
63
|
+
state_dict["execution_log"] = execution_log
|
|
64
|
+
self._states[resource_key] = state_dict
|
|
58
65
|
|
|
59
66
|
|
|
60
67
|
class PostgresStorage(CircuitBreakerStorage):
|
|
@@ -67,11 +74,11 @@ class PostgresStorage(CircuitBreakerStorage):
|
|
|
67
74
|
self.connection_string = connection_string
|
|
68
75
|
self.namespace = namespace
|
|
69
76
|
self._ensure_table_exists()
|
|
70
|
-
|
|
77
|
+
|
|
71
78
|
def _get_connection(self) -> Connection:
|
|
72
79
|
"""Get a database connection."""
|
|
73
80
|
return psycopg.connect(self.connection_string)
|
|
74
|
-
|
|
81
|
+
|
|
75
82
|
def _ensure_table_exists(self) -> None:
|
|
76
83
|
"""Ensure the circuit breaker table exists with namespace support."""
|
|
77
84
|
try:
|
|
@@ -154,7 +161,7 @@ class PostgresStorage(CircuitBreakerStorage):
|
|
|
154
161
|
except Exception as e:
|
|
155
162
|
logger.error(f"Failed to ensure table exists: {e}")
|
|
156
163
|
raise
|
|
157
|
-
|
|
164
|
+
|
|
158
165
|
def get_state(self, resource_key: str) -> Optional[Dict[str, Any]]:
|
|
159
166
|
"""Get the state for a given resource key within this namespace.
|
|
160
167
|
|
|
@@ -166,7 +173,7 @@ class PostgresStorage(CircuitBreakerStorage):
|
|
|
166
173
|
with self._get_connection() as conn:
|
|
167
174
|
with conn.cursor() as cur:
|
|
168
175
|
cur.execute(
|
|
169
|
-
"SELECT state, failure_count, open_until "
|
|
176
|
+
"SELECT state, failure_count, open_until, execution_log "
|
|
170
177
|
"FROM rc_circuit_breakers "
|
|
171
178
|
"WHERE resource_key = %s AND namespace = %s "
|
|
172
179
|
"FOR UPDATE",
|
|
@@ -174,19 +181,32 @@ class PostgresStorage(CircuitBreakerStorage):
|
|
|
174
181
|
)
|
|
175
182
|
row = cur.fetchone()
|
|
176
183
|
if row:
|
|
177
|
-
|
|
184
|
+
result = {
|
|
178
185
|
"state": row[0],
|
|
179
186
|
"failure_count": row[1],
|
|
180
187
|
"open_until": row[2].timestamp() if row[2] else 0
|
|
181
188
|
}
|
|
189
|
+
# Add execution_log if present
|
|
190
|
+
if row[3] is not None:
|
|
191
|
+
result["execution_log"] = row[3]
|
|
192
|
+
return result
|
|
182
193
|
return None
|
|
183
194
|
except Exception as e:
|
|
184
195
|
logger.error(f"Failed to get state for {resource_key} (namespace={self.namespace}): {e}")
|
|
185
196
|
raise
|
|
186
|
-
|
|
187
|
-
def set_state(self, resource_key: str, state: str, failure_count: int, open_until: float) -> None:
|
|
188
|
-
"""Set the state for a given resource key within this namespace.
|
|
197
|
+
|
|
198
|
+
def set_state(self, resource_key: str, state: str, failure_count: int, open_until: float, execution_log: Optional[list] = None) -> None:
|
|
199
|
+
"""Set the state for a given resource key within this namespace.
|
|
200
|
+
|
|
201
|
+
Args:
|
|
202
|
+
resource_key: Unique circuit breaker identifier
|
|
203
|
+
state: Circuit state (CLOSED, OPEN, HALF_OPEN)
|
|
204
|
+
failure_count: Number of consecutive failures
|
|
205
|
+
open_until: Timestamp when circuit can transition from OPEN
|
|
206
|
+
execution_log: Optional list of boolean success/failure results for the circular buffer
|
|
207
|
+
"""
|
|
189
208
|
try:
|
|
209
|
+
import json
|
|
190
210
|
with self._get_connection() as conn:
|
|
191
211
|
with conn.cursor() as cur:
|
|
192
212
|
# Convert timestamp to PostgreSQL timestamp
|
|
@@ -194,19 +214,40 @@ class PostgresStorage(CircuitBreakerStorage):
|
|
|
194
214
|
if open_until > 0:
|
|
195
215
|
open_until_ts = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(open_until))
|
|
196
216
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
217
|
+
# Serialize execution_log to JSON if provided
|
|
218
|
+
execution_log_json = json.dumps(execution_log) if execution_log is not None else None
|
|
219
|
+
|
|
220
|
+
if execution_log is not None:
|
|
221
|
+
# Update including execution_log
|
|
222
|
+
cur.execute(
|
|
223
|
+
"""
|
|
224
|
+
INSERT INTO rc_circuit_breakers
|
|
225
|
+
(resource_key, namespace, state, failure_count, open_until, execution_log)
|
|
226
|
+
VALUES (%s, %s, %s, %s, %s, %s::jsonb)
|
|
227
|
+
ON CONFLICT (resource_key, namespace) DO UPDATE SET
|
|
228
|
+
state = EXCLUDED.state,
|
|
229
|
+
failure_count = EXCLUDED.failure_count,
|
|
230
|
+
open_until = EXCLUDED.open_until,
|
|
231
|
+
execution_log = EXCLUDED.execution_log,
|
|
232
|
+
updated_at = CURRENT_TIMESTAMP
|
|
233
|
+
""",
|
|
234
|
+
(resource_key, self.namespace, state, failure_count, open_until_ts, execution_log_json)
|
|
235
|
+
)
|
|
236
|
+
else:
|
|
237
|
+
# Update without execution_log (preserve existing value)
|
|
238
|
+
cur.execute(
|
|
239
|
+
"""
|
|
240
|
+
INSERT INTO rc_circuit_breakers
|
|
241
|
+
(resource_key, namespace, state, failure_count, open_until)
|
|
242
|
+
VALUES (%s, %s, %s, %s, %s)
|
|
243
|
+
ON CONFLICT (resource_key, namespace) DO UPDATE SET
|
|
244
|
+
state = EXCLUDED.state,
|
|
245
|
+
failure_count = EXCLUDED.failure_count,
|
|
246
|
+
open_until = EXCLUDED.open_until,
|
|
247
|
+
updated_at = CURRENT_TIMESTAMP
|
|
248
|
+
""",
|
|
249
|
+
(resource_key, self.namespace, state, failure_count, open_until_ts)
|
|
250
|
+
)
|
|
210
251
|
conn.commit()
|
|
211
252
|
except Exception as e:
|
|
212
253
|
logger.error(f"Failed to set state for {resource_key} (namespace={self.namespace}): {e}")
|
|
@@ -249,4 +290,4 @@ def create_storage(namespace: Optional[str] = None) -> CircuitBreakerStorage:
|
|
|
249
290
|
else:
|
|
250
291
|
# Default to in-memory storage
|
|
251
292
|
logger.info(f"Using in-memory storage for circuit breaker (no PostgreSQL config found), namespace={namespace}")
|
|
252
|
-
return InMemoryStorage()
|
|
293
|
+
return InMemoryStorage()
|
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: resilient-circuit
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.2
|
|
4
4
|
Summary: A resilient circuit breaker and retry library with PostgreSQL support for distributed systems
|
|
5
5
|
Author-email: Farshid Ashouri <farsheed.ashouri@gmail.com>
|
|
6
|
-
License: Apache
|
|
6
|
+
License-Expression: Apache-2.0
|
|
7
7
|
Project-URL: Homepage, https://github.com/rodmena-limited/resilient-circuit
|
|
8
8
|
Project-URL: Repository, https://github.com/rodmena-limited/resilient-circuit
|
|
9
9
|
Project-URL: Documentation, https://resilient-circuit.readthedocs.io
|
|
10
10
|
Classifier: Development Status :: 4 - Beta
|
|
11
11
|
Classifier: Intended Audience :: Developers
|
|
12
|
-
Classifier: License :: OSI Approved :: Apache Software License
|
|
13
12
|
Classifier: Programming Language :: Python :: 3
|
|
14
13
|
Classifier: Programming Language :: Python :: 3.9
|
|
15
14
|
Classifier: Programming Language :: Python :: 3.10
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
resilient_circuit/__init__.py,sha256=Z3WOzwCT5fP0VptBNu-bkrbKGGljaDB9QL9FwkcBpQI,552
|
|
2
2
|
resilient_circuit/backoff.py,sha256=aLSFYqU_CROrkDaepZaTF_vUy05fBvRhUruXe3Wcmlk,1148
|
|
3
3
|
resilient_circuit/buffer.py,sha256=UIOxPgo07TmveTbaSSJXHzDZwu_aYRVkgIWbGuMhH-8,1385
|
|
4
|
-
resilient_circuit/circuit_breaker.py,sha256=
|
|
5
|
-
resilient_circuit/cli.py,sha256=
|
|
4
|
+
resilient_circuit/circuit_breaker.py,sha256=yawdm0bJz11kqeIs5UIWlCJehQeF1t9X2E1Pvo1HkDY,14472
|
|
5
|
+
resilient_circuit/cli.py,sha256=_XdXdPaCewVL0Tg6MP-zRLWx7qpbJ54ysMnUht6nEBw,9534
|
|
6
6
|
resilient_circuit/exceptions.py,sha256=f1A9hK1y9kxOFtzldIMCOy0Ui9cKnLuCkUzPUU7zyR4,162
|
|
7
7
|
resilient_circuit/failsafe.py,sha256=B6piP5gWAn88maJnk_Gp3nSRp0FCCtZ4Lnd7Ekrgo-M,1068
|
|
8
8
|
resilient_circuit/policy.py,sha256=MJeETZKw_oMrRkgfq160nrFrSm6tv1N2pgkGxsaQmRs,289
|
|
9
9
|
resilient_circuit/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
|
-
resilient_circuit/retry.py,sha256=
|
|
11
|
-
resilient_circuit/storage.py,sha256=
|
|
12
|
-
resilient_circuit-0.4.
|
|
13
|
-
resilient_circuit-0.4.
|
|
14
|
-
resilient_circuit-0.4.
|
|
15
|
-
resilient_circuit-0.4.
|
|
16
|
-
resilient_circuit-0.4.
|
|
17
|
-
resilient_circuit-0.4.
|
|
10
|
+
resilient_circuit/retry.py,sha256=DYeqVLHfMszM7UiV89rxJ7QA_eRofJwz7vmOeSTtJwM,1456
|
|
11
|
+
resilient_circuit/storage.py,sha256=1eyhDnP5jReZIpzsIvVrn7HBet_lJFCVaJoe6CTFh1M,13150
|
|
12
|
+
resilient_circuit-0.4.2.dist-info/licenses/LICENSE,sha256=QwcOLU5TJoTeUhuIXzhdCEEDDvorGiC6-3YTOl4TecE,11356
|
|
13
|
+
resilient_circuit-0.4.2.dist-info/METADATA,sha256=uHeP4j7TX_WcxMh4yDYbJEipWN2ENqD0jtMqJieRpD8,14692
|
|
14
|
+
resilient_circuit-0.4.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
15
|
+
resilient_circuit-0.4.2.dist-info/entry_points.txt,sha256=JbXU75gDeU3PsVykHG7Bq1lYJG7hr5KG-3ny-HGCcZI,65
|
|
16
|
+
resilient_circuit-0.4.2.dist-info/top_level.txt,sha256=SFJWwSDlbsmTWV8F67fdmfAK_dtTT9c8L-kfdnior4s,18
|
|
17
|
+
resilient_circuit-0.4.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|