This repository was archived by the owner on May 5, 2022. It is now read-only.
forked from launchdarkly/python-server-sdk
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathstreaming.py
More file actions
89 lines (77 loc) · 3.23 KB
/
Copy pathstreaming.py
File metadata and controls
89 lines (77 loc) · 3.23 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
from __future__ import absolute_import
import json
from threading import Thread
import backoff
from ldclient.interfaces import UpdateProcessor
from ldclient.sse_client import SSEClient
from ldclient.util import _stream_headers, log
# allows for up to 5 minutes to elapse without any data sent across the stream. The heartbeats sent as comments on the
# stream will keep this from triggering
stream_read_timeout = 5 * 60
class StreamingUpdateProcessor(Thread, UpdateProcessor):
def __init__(self, config, requester, store, ready):
Thread.__init__(self)
self.daemon = True
self._uri = config.stream_uri
self._config = config
self._requester = requester
self._store = store
self._running = False
self._ready = ready
def run(self):
log.info("Starting StreamingUpdateProcessor connecting to uri: " + self._uri)
self._running = True
while self._running:
self._connect()
def _backoff_expo():
return backoff.expo(max_value=30)
@backoff.on_exception(_backoff_expo, BaseException, max_tries=None, jitter=backoff.full_jitter)
def _connect(self):
messages = SSEClient(
self._uri,
verify=self._config.verify_ssl,
headers=_stream_headers(self._config.sdk_key),
connect_timeout=self._config.connect_timeout,
read_timeout=stream_read_timeout)
for msg in messages:
if not self._running:
break
message_ok = self.process_message(self._store, self._requester, msg, self._ready)
if message_ok is True and self._ready.is_set() is False:
self._ready.set()
def stop(self):
log.info("Stopping StreamingUpdateProcessor")
self._running = False
def initialized(self):
return self._running and self._ready.is_set() is True and self._store.initialized is True
@staticmethod
def process_message(store, requester, msg, ready):
log.debug("Received stream event {0} with data: {1}".format(msg.event, msg.data))
if msg.event == 'put':
payload = json.loads(msg.data)
store.init(payload)
if not ready.is_set() is True and store.initialized is True:
log.info("StreamingUpdateProcessor initialized ok")
return True
elif msg.event == 'patch':
payload = json.loads(msg.data)
key = payload['path'][1:]
feature = payload['data']
store.upsert(key, feature)
elif msg.event == "indirect/patch":
key = msg.data
store.upsert(key, requester.get_one(key))
elif msg.event == "indirect/put":
store.init(requester.get_all())
if not ready.is_set() is True and store.initialized is True:
log.info("StreamingUpdateProcessor initialized ok")
return True
elif msg.event == 'delete':
payload = json.loads(msg.data)
key = payload['path'][1:]
# noinspection PyShadowingNames
version = payload['version']
store.delete(key, version)
else:
log.warning('Unhandled event in stream processor: ' + msg.event)
return False