Skip to content

Commit b331c4a

Browse files
committed
Add error handling and timeouts
1 parent 5e70ca8 commit b331c4a

File tree

3 files changed

+49
-11
lines changed

3 files changed

+49
-11
lines changed

posthog/client.py

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,9 @@ def _load_feature_flags(self):
240240
'To use feature flags, please set a personal_api_key ' \
241241
'More information: https://posthog.com/docs/api/overview'
242242
)
243+
except Exception as e:
244+
self.log.warning('[FEATURE FLAGS] Fetching feature flags failed with following error. We will retry in %s seconds.' % self.poll_interval)
245+
self.log.warning(e)
243246

244247
self._last_feature_flag_poll = datetime.utcnow().replace(tzinfo=tzutc())
245248

@@ -251,20 +254,32 @@ def load_feature_flags(self):
251254
def feature_enabled(self, key, distinct_id, default=False):
252255
require('key', key, string_types)
253256
require('distinct_id', distinct_id, ID_TYPES)
257+
error = False
254258

255259
if not self.feature_flags:
256260
self.load_feature_flags()
257261

258-
try:
259-
feature_flag = [flag for flag in self.feature_flags if flag['key'] == key][0]
260-
except IndexError:
261-
return default
262-
263-
if feature_flag.get('is_simple_flag'):
264-
response = _hash(key, distinct_id) <= (feature_flag['rollout_percentage'] / 100)
262+
# If loading in previous line failed
263+
if not self.feature_flags:
264+
response = default
265+
error = True
265266
else:
266-
request = get(self.api_key, '/decide/', self.host)
267-
response = key in request['featureFlags']
267+
try:
268+
feature_flag = [flag for flag in self.feature_flags if flag['key'] == key][0]
269+
except IndexError:
270+
return default
271+
272+
if feature_flag.get('is_simple_flag'):
273+
response = _hash(key, distinct_id) <= (feature_flag['rollout_percentage'] / 100)
274+
else:
275+
try:
276+
request = get(self.api_key, '/decide/', self.host, timeout=1)
277+
response = key in request['featureFlags']
278+
except Exception as e:
279+
response = default
280+
self.log.warning('[FEATURE FLAGS] Unable to get data for flag %s, because of the following error:' % key)
281+
self.log.warning(e)
282+
error = True
268283

269284
self.capture(distinct_id, '$feature_flag_called', {'$feature_flag': key, '$feature_flag_response': response})
270285
return response

posthog/request.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ def post(api_key, host=None, gzip=False, timeout=15, **kwargs):
5151
except ValueError:
5252
raise APIError(res.status_code, res.text)
5353

54-
def get(api_key, url, host=None):
54+
def get(api_key, url, host=None, timeout=None):
5555
log = logging.getLogger('posthog')
5656
url = remove_trailing_slash(host or DEFAULT_HOST) + url
5757
response = requests.get(
@@ -60,6 +60,7 @@ def get(api_key, url, host=None):
6060
'Authorization': 'Bearer %s' % api_key,
6161
'User-Agent': USER_AGENT
6262
},
63+
timeout=timeout
6364
)
6465
if response.status_code == 200:
6566
return response.json()

posthog/test/client.py

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -280,4 +280,26 @@ def test_feature_enabled_doesnt_exist(self, patch_get):
280280
client.feature_flags = []
281281

282282
self.assertFalse(client.feature_enabled('doesnt-exist', 'distinct_id'))
283-
self.assertTrue(client.feature_enabled('doesnt-exist', 'distinct_id', True))
283+
self.assertTrue(client.feature_enabled('doesnt-exist', 'distinct_id', True))
284+
285+
@mock.patch('posthog.client.Poller')
286+
@mock.patch('posthog.client.get')
287+
def test_load_feature_flags_error(self, patch_get, patch_poll):
288+
def raise_effect():
289+
raise Exception('http exception')
290+
patch_get.return_value.raiseError.side_effect = raise_effect
291+
client = Client(TEST_API_KEY, personal_api_key='test')
292+
client.feature_flags = []
293+
294+
self.assertFalse(client.feature_enabled('doesnt-exist', 'distinct_id'))
295+
296+
@mock.patch('posthog.client.Poller')
297+
@mock.patch('posthog.client.get')
298+
def test_call_identify_fails(self, patch_get, patch_poll):
299+
def raise_effect():
300+
raise Exception('http exception')
301+
patch_get.return_value.raiseError.side_effect = raise_effect
302+
client = Client(TEST_API_KEY, personal_api_key='test')
303+
client.feature_flags = [{'key': 'example', 'is_simple_flag': False}]
304+
305+
self.assertFalse(client.feature_enabled('example', 'distinct_id'))

0 commit comments

Comments
 (0)