Skip to content

Commit f79bf8b

Browse files
authored
Merge branch 'Feature/Async' into async-sync-segment-workerpool
2 parents 6c8e0c9 + 8580abb commit f79bf8b

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+4684
-857
lines changed

splitio/api/auth.py

Lines changed: 47 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44
import json
55

66
from splitio.api import APIException, headers_from_metadata
7-
from splitio.api.commons import record_telemetry
8-
from splitio.util.time import get_current_epoch_time_ms
97
from splitio.api.client import HttpClientException
108
from splitio.models.token import from_raw
119
from splitio.models.telemetry import HTTPExceptionsAndLatencies
@@ -31,6 +29,7 @@ def __init__(self, client, sdk_key, sdk_metadata, telemetry_runtime_producer):
3129
self._sdk_key = sdk_key
3230
self._metadata = headers_from_metadata(sdk_metadata)
3331
self._telemetry_runtime_producer = telemetry_runtime_producer
32+
self._client.set_telemetry_data(HTTPExceptionsAndLatencies.TOKEN, self._telemetry_runtime_producer)
3433

3534
def authenticate(self):
3635
"""
@@ -39,15 +38,13 @@ def authenticate(self):
3938
:return: Json representation of an authentication.
4039
:rtype: splitio.models.token.Token
4140
"""
42-
start = get_current_epoch_time_ms()
4341
try:
4442
response = self._client.get(
4543
'auth',
4644
'v2/auth',
4745
self._sdk_key,
4846
extra_headers=self._metadata,
4947
)
50-
record_telemetry(response.status_code, get_current_epoch_time_ms() - start, HTTPExceptionsAndLatencies.TOKEN, self._telemetry_runtime_producer)
5148
if 200 <= response.status_code < 300:
5249
payload = json.loads(response.body)
5350
return from_raw(payload)
@@ -59,3 +56,49 @@ def authenticate(self):
5956
_LOGGER.error('Exception raised while authenticating')
6057
_LOGGER.debug('Exception information: ', exc_info=True)
6158
raise APIException('Could not perform authentication.') from exc
59+
60+
class AuthAPIAsync(object): # pylint: disable=too-few-public-methods
61+
"""Async Class that uses an httpClient to communicate with the SDK Auth Service API."""
62+
63+
def __init__(self, client, sdk_key, sdk_metadata, telemetry_runtime_producer):
64+
"""
65+
Class constructor.
66+
67+
:param client: HTTP Client responsble for issuing calls to the backend.
68+
:type client: HttpClient
69+
:param sdk_key: User sdk key.
70+
:type sdk_key: string
71+
:param sdk_metadata: SDK version & machine name & IP.
72+
:type sdk_metadata: splitio.client.util.SdkMetadata
73+
"""
74+
self._client = client
75+
self._sdk_key = sdk_key
76+
self._metadata = headers_from_metadata(sdk_metadata)
77+
self._telemetry_runtime_producer = telemetry_runtime_producer
78+
self._client.set_telemetry_data(HTTPExceptionsAndLatencies.TOKEN, self._telemetry_runtime_producer)
79+
80+
async def authenticate(self):
81+
"""
82+
Perform authentication.
83+
84+
:return: Json representation of an authentication.
85+
:rtype: splitio.models.token.Token
86+
"""
87+
try:
88+
response = await self._client.get(
89+
'auth',
90+
'v2/auth',
91+
self._sdk_key,
92+
extra_headers=self._metadata,
93+
)
94+
if 200 <= response.status_code < 300:
95+
payload = json.loads(response.body)
96+
return from_raw(payload)
97+
else:
98+
if (response.status_code >= 400 and response.status_code < 500):
99+
await self._telemetry_runtime_producer.record_auth_rejections()
100+
raise APIException(response.body, response.status_code, response.headers)
101+
except HttpClientException as exc:
102+
_LOGGER.error('Exception raised while authenticating')
103+
_LOGGER.debug('Exception information: ', exc_info=True)
104+
raise APIException('Could not perform authentication.') from exc

splitio/api/client.py

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import abc
66

77
from splitio.optional.loaders import aiohttp
8+
from splitio.util.time import get_current_epoch_time_ms
89

910
SDK_URL = 'https://sdk.split.io/api'
1011
EVENTS_URL = 'https://events.split.io/api'
@@ -73,6 +74,20 @@ def get(self, server, path, apikey):
7374
def post(self, server, path, apikey):
7475
"""http post request"""
7576

77+
def set_telemetry_data(self, metric_name, telemetry_runtime_producer):
78+
"""
79+
Set the data needed for telemetry call
80+
81+
:param metric_name: metric name for telemetry
82+
:type metric_name: str
83+
84+
:param telemetry_runtime_producer: telemetry recording instance
85+
:type telemetry_runtime_producer: splitio.engine.telemetry.TelemetryRuntimeProducer
86+
"""
87+
self._telemetry_runtime_producer = telemetry_runtime_producer
88+
self._metric_name = metric_name
89+
90+
7691
class HttpClient(HttpClientBase):
7792
"""HttpClient wrapper."""
7893

@@ -116,13 +131,15 @@ def get(self, server, path, sdk_key, query=None, extra_headers=None): # pylint:
116131
if extra_headers is not None:
117132
headers.update(extra_headers)
118133

134+
start = get_current_epoch_time_ms()
119135
try:
120136
response = requests.get(
121137
_build_url(server, path, self._urls),
122138
params=query,
123139
headers=headers,
124140
timeout=self._timeout
125141
)
142+
self._record_telemetry(response.status_code, get_current_epoch_time_ms() - start)
126143
return HttpResponse(response.status_code, response.text, response.headers)
127144
except Exception as exc: # pylint: disable=broad-except
128145
raise HttpClientException('requests library is throwing exceptions') from exc
@@ -152,6 +169,7 @@ def post(self, server, path, sdk_key, body, query=None, extra_headers=None): #
152169
if extra_headers is not None:
153170
headers.update(extra_headers)
154171

172+
start = get_current_epoch_time_ms()
155173
try:
156174
response = requests.post(
157175
_build_url(server, path, self._urls),
@@ -160,10 +178,28 @@ def post(self, server, path, sdk_key, body, query=None, extra_headers=None): #
160178
headers=headers,
161179
timeout=self._timeout
162180
)
181+
self._record_telemetry(response.status_code, get_current_epoch_time_ms() - start)
163182
return HttpResponse(response.status_code, response.text, response.headers)
164183
except Exception as exc: # pylint: disable=broad-except
165184
raise HttpClientException('requests library is throwing exceptions') from exc
166185

186+
def _record_telemetry(self, status_code, elapsed):
187+
"""
188+
Record Telemetry info
189+
190+
:param status_code: http request status code
191+
:type status_code: int
192+
193+
:param elapsed: response time elapsed.
194+
:type status_code: int
195+
"""
196+
self._telemetry_runtime_producer.record_sync_latency(self._metric_name, elapsed)
197+
if 200 <= status_code < 300:
198+
self._telemetry_runtime_producer.record_successful_sync(self._metric_name, get_current_epoch_time_ms())
199+
return
200+
self._telemetry_runtime_producer.record_sync_error(self._metric_name, status_code)
201+
202+
167203
class HttpClientAsync(HttpClientBase):
168204
"""HttpClientAsync wrapper."""
169205

@@ -204,6 +240,7 @@ async def get(self, server, path, apikey, query=None, extra_headers=None): # py
204240
headers = _build_basic_headers(apikey)
205241
if extra_headers is not None:
206242
headers.update(extra_headers)
243+
start = get_current_epoch_time_ms()
207244
try:
208245
async with self._session.get(
209246
_build_url(server, path, self._urls),
@@ -212,6 +249,7 @@ async def get(self, server, path, apikey, query=None, extra_headers=None): # py
212249
timeout=self._timeout
213250
) as response:
214251
body = await response.text()
252+
await self._record_telemetry(response.status, get_current_epoch_time_ms() - start)
215253
return HttpResponse(response.status, body, response.headers)
216254
except aiohttp.ClientError as exc: # pylint: disable=broad-except
217255
raise HttpClientException('aiohttp library is throwing exceptions') from exc
@@ -237,6 +275,7 @@ async def post(self, server, path, apikey, body, query=None, extra_headers=None)
237275
headers = _build_basic_headers(apikey)
238276
if extra_headers is not None:
239277
headers.update(extra_headers)
278+
start = get_current_epoch_time_ms()
240279
try:
241280
async with self._session.post(
242281
_build_url(server, path, self._urls),
@@ -246,6 +285,23 @@ async def post(self, server, path, apikey, body, query=None, extra_headers=None)
246285
timeout=self._timeout
247286
) as response:
248287
body = await response.text()
288+
await self._record_telemetry(response.status, get_current_epoch_time_ms() - start)
249289
return HttpResponse(response.status, body, response.headers)
250290
except aiohttp.ClientError as exc: # pylint: disable=broad-except
251-
raise HttpClientException('aiohttp library is throwing exceptions') from exc
291+
raise HttpClientException('aiohttp library is throwing exceptions') from exc
292+
293+
async def _record_telemetry(self, status_code, elapsed):
294+
"""
295+
Record Telemetry info
296+
297+
:param status_code: http request status code
298+
:type status_code: int
299+
300+
:param elapsed: response time elapsed.
301+
:type status_code: int
302+
"""
303+
await self._telemetry_runtime_producer.record_sync_latency(self._metric_name, elapsed)
304+
if 200 <= status_code < 300:
305+
await self._telemetry_runtime_producer.record_successful_sync(self._metric_name, get_current_epoch_time_ms())
306+
return
307+
await self._telemetry_runtime_producer.record_sync_error(self._metric_name, status_code)

splitio/api/commons.py

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,8 @@
11
"""Commons module."""
2-
from splitio.util.time import get_current_epoch_time_ms
32

43
_CACHE_CONTROL = 'Cache-Control'
54
_CACHE_CONTROL_NO_CACHE = 'no-cache'
65

7-
def record_telemetry(status_code, elapsed, metric_name, telemetry_runtime_producer):
8-
"""
9-
Record Telemetry info
10-
11-
:param status_code: http request status code
12-
:type status_code: int
13-
14-
:param elapsed: response time elapsed.
15-
:type status_code: int
16-
17-
:param metric_name: metric name for telemetry
18-
:type metric_name: str
19-
20-
:param telemetry_runtime_producer: telemetry recording instance
21-
:type telemetry_runtime_producer: splitio.engine.telemetry.TelemetryRuntimeProducer
22-
"""
23-
telemetry_runtime_producer.record_sync_latency(metric_name, elapsed)
24-
if 200 <= status_code < 300:
25-
telemetry_runtime_producer.record_successful_sync(metric_name, get_current_epoch_time_ms())
26-
return
27-
telemetry_runtime_producer.record_sync_error(metric_name, status_code)
28-
296
class FetchOptions(object):
307
"""Fetch Options object."""
318

splitio/api/events.py

Lines changed: 69 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -4,32 +4,14 @@
44

55
from splitio.api import APIException, headers_from_metadata
66
from splitio.api.client import HttpClientException
7-
from splitio.api.commons import record_telemetry
8-
from splitio.util.time import get_current_epoch_time_ms
97
from splitio.models.telemetry import HTTPExceptionsAndLatencies
108

119

1210
_LOGGER = logging.getLogger(__name__)
1311

1412

15-
class EventsAPI(object): # pylint: disable=too-few-public-methods
16-
"""Class that uses an httpClient to communicate with the events API."""
17-
18-
def __init__(self, http_client, sdk_key, sdk_metadata, telemetry_runtime_producer):
19-
"""
20-
Class constructor.
21-
22-
:param http_client: HTTP Client responsble for issuing calls to the backend.
23-
:type http_client: HttpClient
24-
:param sdk_key: sdk key.
25-
:type sdk_key: string
26-
:param sdk_metadata: SDK version & machine name & IP.
27-
:type sdk_metadata: splitio.client.util.SdkMetadata
28-
"""
29-
self._client = http_client
30-
self._sdk_key = sdk_key
31-
self._metadata = headers_from_metadata(sdk_metadata)
32-
self._telemetry_runtime_producer = telemetry_runtime_producer
13+
class EventsAPIBase(object): # pylint: disable=too-few-public-methods
14+
"""Base Class that uses an httpClient to communicate with the events API."""
3315

3416
@staticmethod
3517
def _build_bulk(events):
@@ -54,6 +36,27 @@ def _build_bulk(events):
5436
for event in events
5537
]
5638

39+
40+
class EventsAPI(EventsAPIBase): # pylint: disable=too-few-public-methods
41+
"""Class that uses an httpClient to communicate with the events API."""
42+
43+
def __init__(self, http_client, sdk_key, sdk_metadata, telemetry_runtime_producer):
44+
"""
45+
Class constructor.
46+
47+
:param http_client: HTTP Client responsble for issuing calls to the backend.
48+
:type http_client: HttpClient
49+
:param sdk_key: sdk key.
50+
:type sdk_key: string
51+
:param sdk_metadata: SDK version & machine name & IP.
52+
:type sdk_metadata: splitio.client.util.SdkMetadata
53+
"""
54+
self._client = http_client
55+
self._sdk_key = sdk_key
56+
self._metadata = headers_from_metadata(sdk_metadata)
57+
self._telemetry_runtime_producer = telemetry_runtime_producer
58+
self._client.set_telemetry_data(HTTPExceptionsAndLatencies.EVENT, self._telemetry_runtime_producer)
59+
5760
def flush_events(self, events):
5861
"""
5962
Send events to the backend.
@@ -65,7 +68,6 @@ def flush_events(self, events):
6568
:rtype: bool
6669
"""
6770
bulk = self._build_bulk(events)
68-
start = get_current_epoch_time_ms()
6971
try:
7072
response = self._client.post(
7173
'events',
@@ -74,7 +76,52 @@ def flush_events(self, events):
7476
body=bulk,
7577
extra_headers=self._metadata,
7678
)
77-
record_telemetry(response.status_code, get_current_epoch_time_ms() - start, HTTPExceptionsAndLatencies.EVENT, self._telemetry_runtime_producer)
79+
if not 200 <= response.status_code < 300:
80+
raise APIException(response.body, response.status_code)
81+
except HttpClientException as exc:
82+
_LOGGER.error('Error posting events because an exception was raised by the HTTPClient')
83+
_LOGGER.debug('Error: ', exc_info=True)
84+
raise APIException('Events not flushed properly.') from exc
85+
86+
class EventsAPIAsync(EventsAPIBase): # pylint: disable=too-few-public-methods
87+
"""Async Class that uses an httpClient to communicate with the events API."""
88+
89+
def __init__(self, http_client, sdk_key, sdk_metadata, telemetry_runtime_producer):
90+
"""
91+
Class constructor.
92+
93+
:param http_client: HTTP Client responsble for issuing calls to the backend.
94+
:type http_client: HttpClient
95+
:param sdk_key: sdk key.
96+
:type sdk_key: string
97+
:param sdk_metadata: SDK version & machine name & IP.
98+
:type sdk_metadata: splitio.client.util.SdkMetadata
99+
"""
100+
self._client = http_client
101+
self._sdk_key = sdk_key
102+
self._metadata = headers_from_metadata(sdk_metadata)
103+
self._telemetry_runtime_producer = telemetry_runtime_producer
104+
self._client.set_telemetry_data(HTTPExceptionsAndLatencies.EVENT, self._telemetry_runtime_producer)
105+
106+
async def flush_events(self, events):
107+
"""
108+
Send events to the backend.
109+
110+
:param events: Events bulk
111+
:type events: list
112+
113+
:return: True if flush was successful. False otherwise
114+
:rtype: bool
115+
"""
116+
bulk = self._build_bulk(events)
117+
try:
118+
response = await self._client.post(
119+
'events',
120+
'events/bulk',
121+
self._sdk_key,
122+
body=bulk,
123+
extra_headers=self._metadata,
124+
)
78125
if not 200 <= response.status_code < 300:
79126
raise APIException(response.body, response.status_code)
80127
except HttpClientException as exc:

0 commit comments

Comments
 (0)