dnaStreaming 2.1.0__tar.gz → 2.2.1__tar.gz
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.
- {dnastreaming-2.1.0/dnaStreaming.egg-info → dnastreaming-2.2.1}/PKG-INFO +2 -2
- dnastreaming-2.2.1/dnaStreaming/demo/show_ha_stream_async.py +36 -0
- {dnastreaming-2.1.0 → dnastreaming-2.2.1}/dnaStreaming/listener.py +68 -1
- dnastreaming-2.2.1/dnaStreaming/services/availability_service.py +87 -0
- dnastreaming-2.2.1/dnaStreaming/services/pubsub_service.py +19 -0
- {dnastreaming-2.1.0 → dnastreaming-2.2.1/dnaStreaming.egg-info}/PKG-INFO +2 -2
- {dnastreaming-2.1.0 → dnastreaming-2.2.1}/dnaStreaming.egg-info/SOURCES.txt +2 -0
- {dnastreaming-2.1.0 → dnastreaming-2.2.1}/setup.py +1 -1
- dnastreaming-2.1.0/dnaStreaming/services/pubsub_service.py +0 -13
- {dnastreaming-2.1.0 → dnastreaming-2.2.1}/LICENSE +0 -0
- {dnastreaming-2.1.0 → dnastreaming-2.2.1}/MANIFEST.in +0 -0
- {dnastreaming-2.1.0 → dnastreaming-2.2.1}/README.rst +0 -0
- {dnastreaming-2.1.0 → dnastreaming-2.2.1}/dnaStreaming/__init__.py +0 -0
- {dnastreaming-2.1.0 → dnastreaming-2.2.1}/dnaStreaming/config.py +0 -0
- {dnastreaming-2.1.0 → dnastreaming-2.2.1}/dnaStreaming/customer_config.json +0 -0
- {dnastreaming-2.1.0 → dnastreaming-2.2.1}/dnaStreaming/demo/__init__.py +0 -0
- {dnastreaming-2.1.0 → dnastreaming-2.2.1}/dnaStreaming/demo/pubsub_stream.py +0 -0
- {dnastreaming-2.1.0 → dnastreaming-2.2.1}/dnaStreaming/demo/show_stream.py +0 -0
- {dnastreaming-2.1.0 → dnastreaming-2.2.1}/dnaStreaming/demo/show_stream_async.py +0 -0
- {dnastreaming-2.1.0 → dnastreaming-2.2.1}/dnaStreaming/services/__init__.py +0 -0
- {dnastreaming-2.1.0 → dnastreaming-2.2.1}/dnaStreaming/services/authentication_service.py +0 -0
- {dnastreaming-2.1.0 → dnastreaming-2.2.1}/dnaStreaming/services/credentials_service.py +0 -0
- {dnastreaming-2.1.0 → dnastreaming-2.2.1}/dnaStreaming/test/__init__.py +0 -0
- {dnastreaming-2.1.0 → dnastreaming-2.2.1}/dnaStreaming/test/test_config.py +0 -0
- {dnastreaming-2.1.0 → dnastreaming-2.2.1}/dnaStreaming.egg-info/dependency_links.txt +0 -0
- {dnastreaming-2.1.0 → dnastreaming-2.2.1}/dnaStreaming.egg-info/requires.txt +0 -0
- {dnastreaming-2.1.0 → dnastreaming-2.2.1}/dnaStreaming.egg-info/top_level.txt +0 -0
- {dnastreaming-2.1.0 → dnastreaming-2.2.1}/setup.cfg +0 -0
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: dnaStreaming
|
|
3
|
-
Version: 2.1
|
|
3
|
+
Version: 2.2.1
|
|
4
4
|
Summary: Dow Jones DNA Streaming Project
|
|
5
5
|
Home-page: https://github.com/dowjones/dj-dna-streams-python
|
|
6
|
-
Download-URL: https://github.com/dowjones/dj-dna-streams-python/archive/release-2.1.
|
|
6
|
+
Download-URL: https://github.com/dowjones/dj-dna-streams-python/archive/release-2.2.1.tar.gz
|
|
7
7
|
Author: Zachary Kagan
|
|
8
8
|
Author-email: zachary.kagan@dowjones.com
|
|
9
9
|
License: MIT
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from time import sleep
|
|
3
|
+
from dnaStreaming.listener import Listener
|
|
4
|
+
|
|
5
|
+
listener = Listener()
|
|
6
|
+
quiet_demo = os.getenv('QUIET_DEMO', "false") == "true"
|
|
7
|
+
max_secs = 1000
|
|
8
|
+
print("\n[ACTIVITY] Receiving messages (ASYNC) for {} seconds...\n[0]".format(max_secs), end='')
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def callback(message, subscription_id):
|
|
12
|
+
callback.counter += 1
|
|
13
|
+
if not quiet_demo:
|
|
14
|
+
if message['action'] != 'del':
|
|
15
|
+
print('[INFO] [SUBSCRIPTION]: {} [MSG] [{}]: AN: {}, TITLE: {}'.format(subscription_id, callback.counter, message['an'], message['title']))
|
|
16
|
+
else:
|
|
17
|
+
print('[INFO] [SUBSCRIPTION]: {} [MSG] [{}]: AN: {}, *** DELETE ***'.format(subscription_id, callback.counter, message['an']))
|
|
18
|
+
else:
|
|
19
|
+
if callback.counter % 10 == 0:
|
|
20
|
+
print('[{}]'.format(callback.counter), end='')
|
|
21
|
+
else:
|
|
22
|
+
print('.', end='')
|
|
23
|
+
return True
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
callback.counter = 0
|
|
27
|
+
listener_controller = listener.listen_async_ha(callback)
|
|
28
|
+
|
|
29
|
+
# Stop receiving messages after 5 seconds
|
|
30
|
+
for count in range(0, max_secs):
|
|
31
|
+
sleep(1)
|
|
32
|
+
|
|
33
|
+
if listener_controller.listener_is_running():
|
|
34
|
+
listener_controller.stop_listener()
|
|
35
|
+
|
|
36
|
+
print("stop receiving messages")
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import time
|
|
2
2
|
import requests
|
|
3
3
|
import json
|
|
4
|
-
from threading import Thread
|
|
4
|
+
from threading import Thread, Event
|
|
5
5
|
|
|
6
6
|
from google.api_core.exceptions import GoogleAPICallError, NotFound
|
|
7
7
|
from google import pubsub_v1
|
|
@@ -9,7 +9,19 @@ from google import pubsub_v1
|
|
|
9
9
|
from dnaStreaming import logger
|
|
10
10
|
from dnaStreaming.config import Config
|
|
11
11
|
from dnaStreaming.services import pubsub_service, credentials_service
|
|
12
|
+
from dnaStreaming.services.availability_service import MAIN_REGION, BACKUP_REGION, ha_listen
|
|
12
13
|
|
|
14
|
+
class ListenerController(object):
|
|
15
|
+
def __init__(self, thread, stop_event):
|
|
16
|
+
self.thread = thread
|
|
17
|
+
self.stop_event = stop_event
|
|
18
|
+
|
|
19
|
+
def stop_listener(self):
|
|
20
|
+
self.stop_event.set()
|
|
21
|
+
self.thread.join()
|
|
22
|
+
|
|
23
|
+
def listener_is_running(self):
|
|
24
|
+
self.thread.is_alive()
|
|
13
25
|
|
|
14
26
|
class Listener(object):
|
|
15
27
|
DEFAULT_UNLIMITED_MESSAGES = None
|
|
@@ -112,3 +124,58 @@ class Listener(object):
|
|
|
112
124
|
logger.info(
|
|
113
125
|
'Listeners for subscriptions have been configured, set and await message arrival.')
|
|
114
126
|
return subscription
|
|
127
|
+
|
|
128
|
+
def listen_async_ha(self, on_message_callback):
|
|
129
|
+
def ack_message_and_callback(message, subscription_path):
|
|
130
|
+
pubsub_msg = json.loads(message.data)
|
|
131
|
+
logger.info("Received news message with ID: {}".format(
|
|
132
|
+
pubsub_msg['data'][0]['id']))
|
|
133
|
+
news_msg = pubsub_msg['data'][0]['attributes']
|
|
134
|
+
short_subscription_id = subscription_path.split("/")[-1]
|
|
135
|
+
on_message_callback(news_msg, short_subscription_id)
|
|
136
|
+
message.ack()
|
|
137
|
+
|
|
138
|
+
main_pubsub_client = pubsub_service.get_client(self.config, MAIN_REGION)
|
|
139
|
+
backup_pubsub_client = pubsub_service.get_client(self.config, BACKUP_REGION)
|
|
140
|
+
|
|
141
|
+
subscription_id = self.config.subscription()
|
|
142
|
+
|
|
143
|
+
if not subscription_id:
|
|
144
|
+
raise Exception(
|
|
145
|
+
'No subscription specified. You must specify the subscription ID either through an environment variable, a config file or '
|
|
146
|
+
'by passing the value to the method.')
|
|
147
|
+
|
|
148
|
+
streaming_credentials = credentials_service.fetch_credentials(
|
|
149
|
+
self.config)
|
|
150
|
+
|
|
151
|
+
api_host = self.config.get_uri_context()
|
|
152
|
+
user_key = self.config.get_user_key()
|
|
153
|
+
|
|
154
|
+
main_subscription_id = subscription_id if "-bak-" not in subscription_id else subscription_id.replace("-filtered-bak-", "-filtered-")
|
|
155
|
+
backup_subscription_id = subscription_id if "-bak-" in subscription_id else subscription_id.replace("-filtered-", "-filtered-bak-")
|
|
156
|
+
|
|
157
|
+
main_subscription_path = main_pubsub_client.subscription_path(
|
|
158
|
+
streaming_credentials['project_id'], main_subscription_id)
|
|
159
|
+
backup_subscription_path = backup_pubsub_client.subscription_path(
|
|
160
|
+
streaming_credentials['project_id'], backup_subscription_id)
|
|
161
|
+
|
|
162
|
+
stop_event = Event()
|
|
163
|
+
listener_thread = Thread(target=ha_listen, daemon=True, args=(
|
|
164
|
+
api_host,
|
|
165
|
+
user_key,
|
|
166
|
+
subscription_id,
|
|
167
|
+
stop_event,
|
|
168
|
+
main_subscription_path,
|
|
169
|
+
backup_subscription_path,
|
|
170
|
+
main_pubsub_client,
|
|
171
|
+
backup_pubsub_client,
|
|
172
|
+
ack_message_and_callback
|
|
173
|
+
))
|
|
174
|
+
listener_thread.start()
|
|
175
|
+
|
|
176
|
+
listener_controller = ListenerController(listener_thread, stop_event)
|
|
177
|
+
|
|
178
|
+
logger.info(
|
|
179
|
+
'Listeners for subscriptions have been configured, set and await message arrival.')
|
|
180
|
+
|
|
181
|
+
return listener_controller
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import requests
|
|
2
|
+
import time
|
|
3
|
+
|
|
4
|
+
from dnaStreaming import logger
|
|
5
|
+
|
|
6
|
+
MAIN_REGION = "us-central1"
|
|
7
|
+
BACKUP_REGION = "us-east5"
|
|
8
|
+
COOLDOWN_PERIOD = 5 * 60
|
|
9
|
+
|
|
10
|
+
def get_active_region(api_host, subscription_id, user_key):
|
|
11
|
+
|
|
12
|
+
try:
|
|
13
|
+
ha_status_url = f"{api_host}/stream_health/get_active_region"
|
|
14
|
+
|
|
15
|
+
headers = {
|
|
16
|
+
"subscription-id": subscription_id,
|
|
17
|
+
"user-key": user_key
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
response = requests.get(url=ha_status_url, headers=headers)
|
|
21
|
+
|
|
22
|
+
assert response.status_code == 200, "Failed to fetch active region"
|
|
23
|
+
|
|
24
|
+
payload = response.json()
|
|
25
|
+
|
|
26
|
+
active_region = payload["data"]["active_region"]
|
|
27
|
+
|
|
28
|
+
return active_region
|
|
29
|
+
|
|
30
|
+
except:
|
|
31
|
+
|
|
32
|
+
return None
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def ha_listen(api_host, user_key, subscription_id, stop_event, main_subscription_path, backup_subscription_path, main_pubsub_client, backup_pubsub_client, callback):
|
|
36
|
+
|
|
37
|
+
# The listener starts reading messages from the main region by default.
|
|
38
|
+
|
|
39
|
+
current_region = MAIN_REGION
|
|
40
|
+
|
|
41
|
+
def wrapped_callback(subscription_path):
|
|
42
|
+
def inner_callback(message):
|
|
43
|
+
callback(message, subscription_path)
|
|
44
|
+
|
|
45
|
+
return inner_callback
|
|
46
|
+
|
|
47
|
+
streaming_pull_future = main_pubsub_client.subscribe(
|
|
48
|
+
main_subscription_path, callback=wrapped_callback(main_subscription_path))
|
|
49
|
+
|
|
50
|
+
while not stop_event.is_set():
|
|
51
|
+
|
|
52
|
+
active_region = get_active_region(api_host, subscription_id, user_key)
|
|
53
|
+
|
|
54
|
+
if active_region is None or active_region not in (MAIN_REGION, BACKUP_REGION):
|
|
55
|
+
logger.warning(f"Got invalid region from API: {active_region}. Listener will keep reading from region {current_region}")
|
|
56
|
+
continue
|
|
57
|
+
|
|
58
|
+
if current_region != active_region:
|
|
59
|
+
|
|
60
|
+
logger.warning(f"Switch event detected in, switching from region {current_region} to region {active_region}")
|
|
61
|
+
|
|
62
|
+
logger.warning(f"Stopping listener in region {current_region}...")
|
|
63
|
+
|
|
64
|
+
# We stop the previous listening process
|
|
65
|
+
streaming_pull_future.cancel()
|
|
66
|
+
streaming_pull_future.result()
|
|
67
|
+
|
|
68
|
+
logger.warning(f"Stopped listener in region {current_region}")
|
|
69
|
+
|
|
70
|
+
logger.warning(f"Starting listener in region {active_region}...")
|
|
71
|
+
|
|
72
|
+
if active_region == MAIN_REGION:
|
|
73
|
+
streaming_pull_future = main_pubsub_client.subscribe(
|
|
74
|
+
main_subscription_path, callback=wrapped_callback(main_subscription_path))
|
|
75
|
+
else: # active_region == BACKUP_REGION
|
|
76
|
+
streaming_pull_future = backup_pubsub_client.subscribe(
|
|
77
|
+
backup_subscription_path, callback=wrapped_callback(backup_subscription_path))
|
|
78
|
+
|
|
79
|
+
logger.warning(f"Started listener in region {active_region}")
|
|
80
|
+
|
|
81
|
+
current_region = active_region
|
|
82
|
+
|
|
83
|
+
# We wait 5 minutes for cooldown for next call to API
|
|
84
|
+
time.sleep(COOLDOWN_PERIOD)
|
|
85
|
+
|
|
86
|
+
streaming_pull_future.cancel()
|
|
87
|
+
streaming_pull_future.result()
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from __future__ import absolute_import, division, print_function
|
|
2
|
+
|
|
3
|
+
from google.cloud.pubsub_v1 import SubscriberClient
|
|
4
|
+
|
|
5
|
+
from dnaStreaming.services import authentication_service
|
|
6
|
+
from dnaStreaming.services import credentials_service
|
|
7
|
+
from dnaStreaming.services.availability_service import MAIN_REGION, BACKUP_REGION
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def get_client(config, region=None):
|
|
11
|
+
streaming_credentials = credentials_service.fetch_credentials(config)
|
|
12
|
+
credentials = authentication_service.get_authenticated_oauth_credentials(streaming_credentials)
|
|
13
|
+
|
|
14
|
+
if region in (MAIN_REGION, BACKUP_REGION):
|
|
15
|
+
client_options = {"api_endpoint": f"{region}-pubsub.googleapis.com:443"}
|
|
16
|
+
else:
|
|
17
|
+
client_options = {"api_endpoint": "pubsub.googleapis.com:443"}
|
|
18
|
+
|
|
19
|
+
return SubscriberClient(credentials=credentials, client_options=client_options)
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: dnaStreaming
|
|
3
|
-
Version: 2.1
|
|
3
|
+
Version: 2.2.1
|
|
4
4
|
Summary: Dow Jones DNA Streaming Project
|
|
5
5
|
Home-page: https://github.com/dowjones/dj-dna-streams-python
|
|
6
|
-
Download-URL: https://github.com/dowjones/dj-dna-streams-python/archive/release-2.1.
|
|
6
|
+
Download-URL: https://github.com/dowjones/dj-dna-streams-python/archive/release-2.2.1.tar.gz
|
|
7
7
|
Author: Zachary Kagan
|
|
8
8
|
Author-email: zachary.kagan@dowjones.com
|
|
9
9
|
License: MIT
|
|
@@ -14,10 +14,12 @@ dnaStreaming.egg-info/requires.txt
|
|
|
14
14
|
dnaStreaming.egg-info/top_level.txt
|
|
15
15
|
dnaStreaming/demo/__init__.py
|
|
16
16
|
dnaStreaming/demo/pubsub_stream.py
|
|
17
|
+
dnaStreaming/demo/show_ha_stream_async.py
|
|
17
18
|
dnaStreaming/demo/show_stream.py
|
|
18
19
|
dnaStreaming/demo/show_stream_async.py
|
|
19
20
|
dnaStreaming/services/__init__.py
|
|
20
21
|
dnaStreaming/services/authentication_service.py
|
|
22
|
+
dnaStreaming/services/availability_service.py
|
|
21
23
|
dnaStreaming/services/credentials_service.py
|
|
22
24
|
dnaStreaming/services/pubsub_service.py
|
|
23
25
|
dnaStreaming/test/__init__.py
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
from __future__ import absolute_import, division, print_function
|
|
2
|
-
|
|
3
|
-
from google.cloud.pubsub_v1 import SubscriberClient
|
|
4
|
-
|
|
5
|
-
from dnaStreaming.services import authentication_service
|
|
6
|
-
from dnaStreaming.services import credentials_service
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
def get_client(config):
|
|
10
|
-
streaming_credentials = credentials_service.fetch_credentials(config)
|
|
11
|
-
credentials = authentication_service.get_authenticated_oauth_credentials(streaming_credentials)
|
|
12
|
-
|
|
13
|
-
return SubscriberClient(credentials=credentials)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|