deltafi 2.7.1__tar.gz → 2.9.0__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.
Potentially problematic release.
This version of deltafi might be problematic. Click here for more details.
- {deltafi-2.7.1 → deltafi-2.9.0}/PKG-INFO +1 -1
- {deltafi-2.7.1 → deltafi-2.9.0}/deltafi/action.py +3 -3
- {deltafi-2.7.1 → deltafi-2.9.0}/deltafi/domain.py +5 -4
- {deltafi-2.7.1 → deltafi-2.9.0}/deltafi/plugin.py +54 -21
- {deltafi-2.7.1 → deltafi-2.9.0}/pyproject.toml +1 -1
- {deltafi-2.7.1 → deltafi-2.9.0}/README.md +0 -0
- {deltafi-2.7.1 → deltafi-2.9.0}/deltafi/__init__.py +0 -0
- {deltafi-2.7.1 → deltafi-2.9.0}/deltafi/actioneventqueue.py +0 -0
- {deltafi-2.7.1 → deltafi-2.9.0}/deltafi/actiontype.py +0 -0
- {deltafi-2.7.1 → deltafi-2.9.0}/deltafi/exception.py +0 -0
- {deltafi-2.7.1 → deltafi-2.9.0}/deltafi/genericmodel.py +0 -0
- {deltafi-2.7.1 → deltafi-2.9.0}/deltafi/input.py +0 -0
- {deltafi-2.7.1 → deltafi-2.9.0}/deltafi/logger.py +0 -0
- {deltafi-2.7.1 → deltafi-2.9.0}/deltafi/metric.py +0 -0
- {deltafi-2.7.1 → deltafi-2.9.0}/deltafi/result.py +0 -0
- {deltafi-2.7.1 → deltafi-2.9.0}/deltafi/storage.py +0 -0
- {deltafi-2.7.1 → deltafi-2.9.0}/deltafi/test_kit/__init__.py +0 -0
- {deltafi-2.7.1 → deltafi-2.9.0}/deltafi/test_kit/assertions.py +0 -0
- {deltafi-2.7.1 → deltafi-2.9.0}/deltafi/test_kit/compare_helpers.py +0 -0
- {deltafi-2.7.1 → deltafi-2.9.0}/deltafi/test_kit/constants.py +0 -0
- {deltafi-2.7.1 → deltafi-2.9.0}/deltafi/test_kit/egress.py +0 -0
- {deltafi-2.7.1 → deltafi-2.9.0}/deltafi/test_kit/framework.py +0 -0
- {deltafi-2.7.1 → deltafi-2.9.0}/deltafi/test_kit/timed_ingress.py +0 -0
- {deltafi-2.7.1 → deltafi-2.9.0}/deltafi/test_kit/transform.py +0 -0
|
@@ -19,12 +19,13 @@
|
|
|
19
19
|
from abc import ABC, abstractmethod
|
|
20
20
|
from typing import Any, List
|
|
21
21
|
|
|
22
|
+
from pydantic import BaseModel
|
|
23
|
+
|
|
22
24
|
from deltafi.actiontype import ActionType
|
|
23
|
-
from deltafi.domain import
|
|
25
|
+
from deltafi.domain import DeltaFileMessage
|
|
24
26
|
from deltafi.genericmodel import GenericModel
|
|
25
27
|
from deltafi.input import EgressInput, TransformInput
|
|
26
28
|
from deltafi.result import *
|
|
27
|
-
from pydantic import BaseModel
|
|
28
29
|
|
|
29
30
|
|
|
30
31
|
class Join(ABC):
|
|
@@ -42,7 +43,6 @@ class Action(ABC):
|
|
|
42
43
|
self.action_type = action_type
|
|
43
44
|
self.description = description
|
|
44
45
|
self.valid_result_types = valid_result_types
|
|
45
|
-
self.action_execution = None
|
|
46
46
|
|
|
47
47
|
@abstractmethod
|
|
48
48
|
def build_input(self, context: Context, delta_file_message: DeltaFileMessage):
|
|
@@ -28,6 +28,7 @@ from deltafi.storage import ContentService, Segment
|
|
|
28
28
|
class ActionExecution(NamedTuple):
|
|
29
29
|
clazz: str
|
|
30
30
|
action: str
|
|
31
|
+
thread_num: int
|
|
31
32
|
did: str
|
|
32
33
|
start_time: datetime
|
|
33
34
|
|
|
@@ -36,7 +37,7 @@ class ActionExecution(NamedTuple):
|
|
|
36
37
|
|
|
37
38
|
@property
|
|
38
39
|
def key(self) -> str:
|
|
39
|
-
return f"{self.clazz}:{self.action}:{self.did}"
|
|
40
|
+
return f"{self.clazz}:{self.action}#{self.thread_num}:{self.did}"
|
|
40
41
|
|
|
41
42
|
|
|
42
43
|
class Context(NamedTuple):
|
|
@@ -163,9 +164,9 @@ class Content:
|
|
|
163
164
|
Content: A deep copy of the Content object.
|
|
164
165
|
"""
|
|
165
166
|
new_copy = Content(name=self.name,
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
167
|
+
segments=copy.deepcopy(self.segments),
|
|
168
|
+
media_type=self.media_type,
|
|
169
|
+
content_service=self.content_service)
|
|
169
170
|
new_copy.add_tags(self.tags.copy())
|
|
170
171
|
return new_copy
|
|
171
172
|
|
|
@@ -33,6 +33,7 @@ from typing import List
|
|
|
33
33
|
|
|
34
34
|
import requests
|
|
35
35
|
import yaml
|
|
36
|
+
|
|
36
37
|
from deltafi.action import Action, Join
|
|
37
38
|
from deltafi.actioneventqueue import ActionEventQueue
|
|
38
39
|
from deltafi.domain import Event, ActionExecution
|
|
@@ -129,6 +130,17 @@ def _setup_content_service():
|
|
|
129
130
|
os.getenv('MINIO_SECRETKEY'))
|
|
130
131
|
|
|
131
132
|
|
|
133
|
+
class ActionThread(object):
|
|
134
|
+
def __init__(self, clazz: Action, thread_num: int, name: str, execution: ActionExecution = None):
|
|
135
|
+
self.clazz = clazz
|
|
136
|
+
self.thread_num = thread_num
|
|
137
|
+
self.name = name
|
|
138
|
+
self.execution = execution
|
|
139
|
+
|
|
140
|
+
def logger_name(self):
|
|
141
|
+
return f"{self.name}#{self.thread_num}"
|
|
142
|
+
|
|
143
|
+
|
|
132
144
|
class PluginCoordinates(object):
|
|
133
145
|
def __init__(self, group_id: str, artifact_id: str, version: str):
|
|
134
146
|
self.group_id = group_id
|
|
@@ -148,7 +160,8 @@ LONG_RUNNING_TASK_DURATION = timedelta(seconds=5)
|
|
|
148
160
|
|
|
149
161
|
class Plugin(object):
|
|
150
162
|
def __init__(self, description: str, plugin_name: str = None, plugin_coordinates: PluginCoordinates = None,
|
|
151
|
-
actions: List = None, action_package: str = None
|
|
163
|
+
actions: List = None, action_package: str = None,
|
|
164
|
+
thread_config: dict = None):
|
|
152
165
|
"""
|
|
153
166
|
Initialize the plugin object
|
|
154
167
|
:param plugin_name: Name of the plugin project
|
|
@@ -157,12 +170,17 @@ class Plugin(object):
|
|
|
157
170
|
environment variables
|
|
158
171
|
:param actions: list of action classes to run
|
|
159
172
|
:param action_package: name of the package containing the actions to run
|
|
173
|
+
:param thread_config: map of action class name and thread count. Actions not found default to 1 thread.
|
|
160
174
|
"""
|
|
161
175
|
self.logger = get_logger()
|
|
162
176
|
|
|
163
177
|
self.content_service = None
|
|
164
178
|
self.queue = None
|
|
165
|
-
self.
|
|
179
|
+
self.singleton_actions = []
|
|
180
|
+
self.action_threads = []
|
|
181
|
+
self.thread_config = {}
|
|
182
|
+
if thread_config is not None:
|
|
183
|
+
self.thread_config = thread_config
|
|
166
184
|
self.core_url = os.getenv('CORE_URL')
|
|
167
185
|
self.image = os.getenv('IMAGE')
|
|
168
186
|
self.image_pull_secret = os.getenv('IMAGE_PULL_SECRET')
|
|
@@ -176,7 +194,7 @@ class Plugin(object):
|
|
|
176
194
|
action_classes.extend(found_actions)
|
|
177
195
|
|
|
178
196
|
unique_actions = dict.fromkeys(action_classes)
|
|
179
|
-
self.
|
|
197
|
+
self.singleton_actions = [action() for action in unique_actions]
|
|
180
198
|
|
|
181
199
|
self.description = description
|
|
182
200
|
self.display_name = os.getenv('PROJECT_NAME') if plugin_name is None else plugin_name
|
|
@@ -191,7 +209,7 @@ class Plugin(object):
|
|
|
191
209
|
else:
|
|
192
210
|
self.hostname = 'UNKNOWN'
|
|
193
211
|
|
|
194
|
-
self.logger.debug(f"Initialized ActionRunner with actions {self.
|
|
212
|
+
self.logger.debug(f"Initialized ActionRunner with actions {self.singleton_actions}")
|
|
195
213
|
|
|
196
214
|
@staticmethod
|
|
197
215
|
def find_actions(package_name) -> List[object]:
|
|
@@ -280,7 +298,7 @@ class Plugin(object):
|
|
|
280
298
|
variables = self.load_variables(flows_path, flow_files)
|
|
281
299
|
|
|
282
300
|
flows = _load__all_resource(flows_path, flow_files)
|
|
283
|
-
actions = [self._action_json(action) for action in self.
|
|
301
|
+
actions = [self._action_json(action) for action in self.singleton_actions]
|
|
284
302
|
|
|
285
303
|
test_files = self.load_integration_tests(tests_path)
|
|
286
304
|
if len(test_files) == 0:
|
|
@@ -317,11 +335,25 @@ class Plugin(object):
|
|
|
317
335
|
|
|
318
336
|
def run(self):
|
|
319
337
|
self.logger.info("Plugin starting")
|
|
320
|
-
|
|
338
|
+
|
|
339
|
+
for action in self.singleton_actions:
|
|
340
|
+
num_threads = 1;
|
|
341
|
+
if self.action_name(action) in self.thread_config:
|
|
342
|
+
maybe_num_threads = self.thread_config[self.action_name(action)]
|
|
343
|
+
if type(maybe_num_threads) == int and maybe_num_threads > 0:
|
|
344
|
+
num_threads = maybe_num_threads
|
|
345
|
+
else:
|
|
346
|
+
self.logger.error(f"Ignoring non-int or invalid thread value {maybe_num_threads}")
|
|
347
|
+
for i in range(num_threads):
|
|
348
|
+
action_thread = ActionThread(action, i, self.action_name(action))
|
|
349
|
+
self.action_threads.append(action_thread)
|
|
350
|
+
|
|
351
|
+
self.queue = _setup_queue(len(self.action_threads) + 1)
|
|
321
352
|
self.content_service = _setup_content_service()
|
|
322
353
|
self._register()
|
|
323
|
-
|
|
324
|
-
|
|
354
|
+
|
|
355
|
+
for action_thread in self.action_threads:
|
|
356
|
+
threading.Thread(target=self._do_action, args=(action_thread,)).start()
|
|
325
357
|
|
|
326
358
|
hb_thread = threading.Thread(target=self._heartbeat)
|
|
327
359
|
hb_thread.start()
|
|
@@ -339,14 +371,14 @@ class Plugin(object):
|
|
|
339
371
|
while True:
|
|
340
372
|
try:
|
|
341
373
|
# Set heartbeats
|
|
342
|
-
for
|
|
343
|
-
self.queue.heartbeat(
|
|
374
|
+
for action_thread in self.action_threads:
|
|
375
|
+
self.queue.heartbeat(action_thread.name)
|
|
344
376
|
|
|
345
377
|
# Record long running tasks
|
|
346
378
|
new_long_running_actions = set()
|
|
347
|
-
for
|
|
348
|
-
|
|
349
|
-
|
|
379
|
+
for action_thread in self.action_threads:
|
|
380
|
+
action_execution = action_thread.execution
|
|
381
|
+
if action_execution and action_execution.exceeds_duration(LONG_RUNNING_TASK_DURATION):
|
|
350
382
|
new_long_running_actions.add(action_execution)
|
|
351
383
|
self.queue.record_long_running_task(action_execution)
|
|
352
384
|
|
|
@@ -378,22 +410,23 @@ class Plugin(object):
|
|
|
378
410
|
response[result.result_key] = result.response()
|
|
379
411
|
return response
|
|
380
412
|
|
|
381
|
-
def _do_action(self,
|
|
382
|
-
action_logger = get_logger(
|
|
413
|
+
def _do_action(self, action_thread: ActionThread):
|
|
414
|
+
action_logger = get_logger(action_thread.logger_name())
|
|
415
|
+
action_logger.info(f"Listening on {action_thread.name}")
|
|
383
416
|
|
|
384
|
-
action_logger.info(f"Listening on {self.action_name(action)}")
|
|
385
417
|
while True:
|
|
386
418
|
try:
|
|
387
|
-
event_string = self.queue.take(
|
|
419
|
+
event_string = self.queue.take(action_thread.name)
|
|
388
420
|
event = Event.create(json.loads(event_string), self.content_service, action_logger)
|
|
389
421
|
start_time = time.time()
|
|
390
422
|
action_logger.debug(f"Processing event for did {event.context.did}")
|
|
391
423
|
|
|
392
|
-
|
|
393
|
-
event.context.did,
|
|
424
|
+
action_thread.execution = ActionExecution(action_thread.name, event.context.action_name,
|
|
425
|
+
action_thread.thread_num, event.context.did,
|
|
426
|
+
datetime.now(timezone.utc))
|
|
394
427
|
|
|
395
428
|
try:
|
|
396
|
-
result =
|
|
429
|
+
result = action_thread.clazz.execute_action(event)
|
|
397
430
|
except ExpectedContentException as e:
|
|
398
431
|
result = ErrorResult(event.context,
|
|
399
432
|
f"Action attempted to look up element {e.index + 1} (index {e.index}) from "
|
|
@@ -407,7 +440,7 @@ class Plugin(object):
|
|
|
407
440
|
result = ErrorResult(event.context,
|
|
408
441
|
f"Action execution {type(e)} exception", f"{str(e)}\n{traceback.format_exc()}")
|
|
409
442
|
|
|
410
|
-
|
|
443
|
+
action_thread.execution = None
|
|
411
444
|
|
|
412
445
|
response = Plugin.to_response(
|
|
413
446
|
event, start_time, time.time(), result)
|
|
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
|