flowcept 0.8.3__py3-none-any.whl → 0.8.5__py3-none-any.whl

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.
@@ -9,13 +9,14 @@ class AutoflushBuffer:
9
9
 
10
10
  def __init__(
11
11
  self,
12
- max_size,
13
- flush_interval,
14
12
  flush_function: Callable,
13
+ max_size=None,
14
+ flush_interval=None,
15
15
  flush_function_args=[],
16
16
  flush_function_kwargs={},
17
17
  ):
18
- self._max_size = max_size
18
+ self._max_size = max_size or float("inf")
19
+ self._flush_interval = flush_interval or float("inf")
19
20
  self._flush_interval = flush_interval
20
21
  self._buffers = [[], []]
21
22
  self._current_buffer_index = 0
@@ -2,9 +2,9 @@
2
2
 
3
3
  from abc import ABC, abstractmethod
4
4
  from typing import Union, List, Callable
5
-
5
+ import csv
6
6
  import msgpack
7
-
7
+ from time import time
8
8
  import flowcept.commons
9
9
  from flowcept.commons.autoflush_buffer import AutoflushBuffer
10
10
 
@@ -19,6 +19,7 @@ from flowcept.configs import (
19
19
  MQ_INSERTION_BUFFER_TIME,
20
20
  MQ_CHUNK_SIZE,
21
21
  MQ_TYPE,
22
+ MQ_TIMING,
22
23
  )
23
24
 
24
25
  from flowcept.commons.utils import GenericJSONEncoder
@@ -69,12 +70,21 @@ class MQDao(ABC):
69
70
  self._keyvalue_dao = KeyValueDAO()
70
71
  self._time_based_flushing_started = False
71
72
  self.buffer: Union[AutoflushBuffer, List] = None
72
- self._flush_events = []
73
+ if MQ_TIMING:
74
+ self._flush_events = []
75
+ self.stop = self._stop_timed
76
+ self.send_message = self._send_message_timed
77
+ self._bulk_publish = self._bulk_publish_timed
78
+ else:
79
+ self.stop = self._stop
73
80
 
74
81
  @abstractmethod
75
82
  def _bulk_publish(self, buffer, channel=MQ_CHANNEL, serializer=msgpack.dumps):
76
83
  raise NotImplementedError()
77
84
 
85
+ def _bulk_publish_timed(self, buffer, channel=MQ_CHANNEL, serializer=msgpack.dumps):
86
+ raise NotImplementedError()
87
+
78
88
  def bulk_publish(self, buffer):
79
89
  """Publish it."""
80
90
  # self.logger.info(f"Going to flush {len(buffer)} to MQ...")
@@ -132,12 +142,10 @@ class MQDao(ABC):
132
142
  """Create the buffer."""
133
143
  if not self.started:
134
144
  if flowcept.configs.DB_FLUSH_MODE == "online":
135
- # msg = "Starting MQ time-based flushing! bundle: "
136
- # self.logger.debug(msg+f"{exec_bundle_id}; interceptor id: {interceptor_instance_id}")
137
145
  self.buffer = AutoflushBuffer(
146
+ flush_function=self.bulk_publish,
138
147
  max_size=MQ_BUFFER_SIZE,
139
148
  flush_interval=MQ_INSERTION_BUFFER_TIME,
140
- flush_function=self.bulk_publish,
141
149
  )
142
150
  self.register_time_based_thread_init(interceptor_instance_id, exec_bundle_id)
143
151
  self._time_based_flushing_started = True
@@ -156,7 +164,18 @@ class MQDao(ABC):
156
164
  self.bulk_publish(self.buffer)
157
165
  self.buffer = list()
158
166
 
159
- def stop(self, interceptor_instance_id: str, bundle_exec_id: int = None):
167
+ def _stop_timed(self, interceptor_instance_id: str, bundle_exec_id: int = None):
168
+ t1 = time()
169
+ self._stop(interceptor_instance_id, bundle_exec_id)
170
+ t2 = time()
171
+ self._flush_events.append(["final", t1, t2, t2 - t1, "n/a"])
172
+
173
+ with open(f"{MQ_TYPE}_{interceptor_instance_id}_{MQ_TYPE}_flush_events.csv", "w", newline="") as file:
174
+ writer = csv.writer(file)
175
+ writer.writerow(["type", "start", "end", "duration", "size"])
176
+ writer.writerows(self._flush_events)
177
+
178
+ def _stop(self, interceptor_instance_id: str, bundle_exec_id: int = None):
160
179
  """Stop it."""
161
180
  msg0 = "MQ publisher received stop signal! bundle: "
162
181
  self.logger.debug(msg0 + f"{bundle_exec_id}; interceptor id: {interceptor_instance_id}")
@@ -189,6 +208,11 @@ class MQDao(ABC):
189
208
  """Send a message."""
190
209
  raise NotImplementedError()
191
210
 
211
+ @abstractmethod
212
+ def _send_message_timed(self, message: dict, channel=MQ_CHANNEL, serializer=msgpack.dumps):
213
+ """Send a message."""
214
+ raise NotImplementedError()
215
+
192
216
  @abstractmethod
193
217
  def message_listener(self, message_handler: Callable):
194
218
  """Get message listener."""
@@ -1,18 +1,15 @@
1
1
  """MQ kafka module."""
2
2
 
3
3
  from typing import Callable
4
-
5
- import msgpack
6
4
  from time import time
5
+ import msgpack
7
6
 
8
7
  from confluent_kafka import Producer, Consumer, KafkaError
9
8
  from confluent_kafka.admin import AdminClient
10
9
 
11
10
  from flowcept.commons.daos.mq_dao.mq_dao_base import MQDao
12
- from flowcept.commons.utils import perf_log
13
11
  from flowcept.configs import (
14
12
  MQ_CHANNEL,
15
- PERF_LOG,
16
13
  MQ_HOST,
17
14
  MQ_PORT,
18
15
  )
@@ -69,26 +66,44 @@ class MQDaoKafka(MQDao):
69
66
  self._producer.produce(channel, key=channel, value=serializer(message))
70
67
  self._producer.flush()
71
68
 
69
+ def _send_message_timed(self, message: dict, channel=MQ_CHANNEL, serializer=msgpack.dumps):
70
+ t1 = time()
71
+ self.send_message(message, channel, serializer)
72
+ t2 = time()
73
+ self._flush_events.append(["single", t1, t2, t2 - t1, len(str(message).encode())])
74
+
72
75
  def _bulk_publish(self, buffer, channel=MQ_CHANNEL, serializer=msgpack.dumps):
76
+ for message in buffer:
77
+ try:
78
+ self._producer.produce(channel, key=channel, value=serializer(message))
79
+ except Exception as e:
80
+ self.logger.exception(e)
81
+ self.logger.error("Some messages couldn't be flushed! Check the messages' contents!")
82
+ self.logger.error(f"Message that caused error: {message}")
83
+ try:
84
+ self._producer.flush()
85
+ self.logger.info(f"Flushed {len(buffer)} msgs to MQ!")
86
+ except Exception as e:
87
+ self.logger.exception(e)
88
+
89
+ def _bulk_publish_timed(self, buffer, channel=MQ_CHANNEL, serializer=msgpack.dumps):
73
90
  total = 0
74
91
  for message in buffer:
75
92
  try:
76
- self.logger.debug(f"Going to send Message:\n\t[BEGIN_MSG]{message}\n[END_MSG]\t")
77
93
  self._producer.produce(channel, key=channel, value=serializer(message))
78
94
  total += len(str(message).encode())
79
95
  except Exception as e:
80
96
  self.logger.exception(e)
81
97
  self.logger.error("Some messages couldn't be flushed! Check the messages' contents!")
82
98
  self.logger.error(f"Message that caused error: {message}")
83
- t0 = 0
84
- if PERF_LOG:
85
- t0 = time()
86
99
  try:
100
+ t1 = time()
87
101
  self._producer.flush()
102
+ t2 = time()
103
+ self._flush_events.append(["bulk", t1, t2, t2 - t1, total])
88
104
  self.logger.info(f"Flushed {len(buffer)} msgs to MQ!")
89
105
  except Exception as e:
90
106
  self.logger.exception(e)
91
- perf_log("mq_pipe_flush", t0)
92
107
 
93
108
  def liveness_test(self):
94
109
  """Get the livelyness of it."""
@@ -9,8 +9,7 @@ import mochi.mofka.client as mofka
9
9
  from mochi.mofka.client import ThreadPool, AdaptiveBatchSize
10
10
 
11
11
  from flowcept.commons.daos.mq_dao.mq_dao_base import MQDao
12
- from flowcept.commons.utils import perf_log
13
- from flowcept.configs import PERF_LOG, MQ_SETTINGS, MQ_CHANNEL
12
+ from flowcept.configs import MQ_SETTINGS, MQ_CHANNEL
14
13
 
15
14
 
16
15
  class MQDaoMofka(MQDao):
@@ -58,7 +57,29 @@ class MQDaoMofka(MQDao):
58
57
  self.producer.push(metadata=message) # using metadata to send data
59
58
  self.producer.flush()
60
59
 
60
+ def _send_message_timed(self, message: dict, channel=MQ_CHANNEL, serializer=msgpack.dumps):
61
+ t1 = time()
62
+ self.send_message(message, channel, serializer)
63
+ t2 = time()
64
+ self._flush_events.append(["single", t1, t2, t2 - t1, len(str(message).encode())])
65
+
61
66
  def _bulk_publish(self, buffer, channel=MQ_CHANNEL, serializer=msgpack.dumps):
67
+ try:
68
+ self.logger.debug(f"Going to send Message:\n\t[BEGIN_MSG]{buffer}\n[END_MSG]\t")
69
+ for m in buffer:
70
+ self.producer.push(m)
71
+
72
+ except Exception as e:
73
+ self.logger.exception(e)
74
+ self.logger.error("Some messages couldn't be flushed! Check the messages' contents!")
75
+ self.logger.error(f"Message that caused error: {buffer}")
76
+ try:
77
+ self.producer.flush()
78
+ self.logger.info(f"Flushed {len(buffer)} msgs to MQ!")
79
+ except Exception as e:
80
+ self.logger.exception(e)
81
+
82
+ def _bulk_publish_timed(self, buffer, channel=MQ_CHANNEL, serializer=msgpack.dumps):
62
83
  total = 0
63
84
  try:
64
85
  self.logger.debug(f"Going to send Message:\n\t[BEGIN_MSG]{buffer}\n[END_MSG]\t")
@@ -71,15 +92,14 @@ class MQDaoMofka(MQDao):
71
92
  self.logger.exception(e)
72
93
  self.logger.error("Some messages couldn't be flushed! Check the messages' contents!")
73
94
  self.logger.error(f"Message that caused error: {buffer}")
74
- t0 = 0
75
- if PERF_LOG:
76
- t0 = time()
77
95
  try:
96
+ t1 = time()
78
97
  self.producer.flush()
98
+ t2 = time()
99
+ self._flush_events.append(["bulk", t1, t2, t2 - t1, total])
79
100
  self.logger.info(f"Flushed {len(buffer)} msgs to MQ!")
80
101
  except Exception as e:
81
102
  self.logger.exception(e)
82
- perf_log("mq_pipe_flush", t0)
83
103
 
84
104
  def liveness_test(self):
85
105
  """Test Mofka Liveness."""
@@ -7,11 +7,7 @@ import msgpack
7
7
  from time import time, sleep
8
8
 
9
9
  from flowcept.commons.daos.mq_dao.mq_dao_base import MQDao
10
- from flowcept.commons.utils import perf_log
11
- from flowcept.configs import (
12
- MQ_CHANNEL,
13
- PERF_LOG,
14
- )
10
+ from flowcept.configs import MQ_CHANNEL
15
11
 
16
12
 
17
13
  class MQDaoRedis(MQDao):
@@ -62,26 +58,47 @@ class MQDaoRedis(MQDao):
62
58
  """Send the message."""
63
59
  self._producer.publish(channel, serializer(message))
64
60
 
61
+ def _send_message_timed(self, message: dict, channel=MQ_CHANNEL, serializer=msgpack.dumps):
62
+ """Send the message using timing for performance evaluation."""
63
+ t1 = time()
64
+ self.send_message(message, channel, serializer)
65
+ t2 = time()
66
+ self._flush_events.append(["single", t1, t2, t2 - t1, len(str(message).encode())])
67
+
65
68
  def _bulk_publish(self, buffer, channel=MQ_CHANNEL, serializer=msgpack.dumps):
69
+ pipe = self._producer.pipeline()
70
+ for message in buffer:
71
+ try:
72
+ pipe.publish(channel, serializer(message))
73
+ except Exception as e:
74
+ self.logger.exception(e)
75
+ self.logger.error("Some messages couldn't be flushed! Check the messages' contents!")
76
+ self.logger.error(f"Message that caused error: {message}")
77
+ try:
78
+ pipe.execute()
79
+ self.logger.debug(f"Flushed {len(buffer)} msgs to MQ!")
80
+ except Exception as e:
81
+ self.logger.exception(e)
82
+
83
+ def _bulk_publish_timed(self, buffer, channel=MQ_CHANNEL, serializer=msgpack.dumps):
66
84
  total = 0
67
85
  pipe = self._producer.pipeline()
68
86
  for message in buffer:
69
87
  try:
70
88
  total += len(str(message).encode())
71
- pipe.publish(MQ_CHANNEL, serializer(message))
89
+ pipe.publish(channel, serializer(message))
72
90
  except Exception as e:
73
91
  self.logger.exception(e)
74
92
  self.logger.error("Some messages couldn't be flushed! Check the messages' contents!")
75
93
  self.logger.error(f"Message that caused error: {message}")
76
- t0 = 0
77
- if PERF_LOG:
78
- t0 = time()
79
94
  try:
95
+ t1 = time()
80
96
  pipe.execute()
97
+ t2 = time()
98
+ self._flush_events.append(["bulk", t1, t2, t2 - t1, total])
81
99
  self.logger.debug(f"Flushed {len(buffer)} msgs to MQ!")
82
100
  except Exception as e:
83
101
  self.logger.exception(e)
84
- perf_log("mq_pipe_execute", t0)
85
102
 
86
103
  def liveness_test(self):
87
104
  """Get the livelyness of it."""
@@ -12,6 +12,7 @@ from flowcept.configs import (
12
12
  SYS_NAME,
13
13
  EXTRA_METADATA,
14
14
  ENVIRONMENT_ID,
15
+ SETTINGS_PATH,
15
16
  )
16
17
 
17
18
 
@@ -23,6 +24,7 @@ class WorkflowObject:
23
24
  workflow_id: AnyStr = None
24
25
  parent_workflow_id: AnyStr = None
25
26
  machine_info: Dict = None
27
+ conf: Dict = None
26
28
  flowcept_settings: Dict = None
27
29
  flowcept_version: AnyStr = None
28
30
  utc_timestamp: float = None
@@ -70,7 +72,7 @@ class WorkflowObject:
70
72
  """Enrich it."""
71
73
  self.utc_timestamp = get_utc_now()
72
74
  self.flowcept_settings = OmegaConf.to_container(settings) if isinstance(settings, DictConfig) else settings
73
-
75
+ self.conf = {"settings_path": SETTINGS_PATH}
74
76
  if adapter_key is not None:
75
77
  # TODO :base-interceptor-refactor: :code-reorg: :usability:
76
78
  # revisit all times we assume settings is not none
flowcept/configs.py CHANGED
@@ -73,9 +73,9 @@ MQ_CHANNEL = settings["mq"].get("channel", "interception")
73
73
  MQ_PASSWORD = settings["mq"].get("password", None)
74
74
  MQ_HOST = os.getenv("MQ_HOST", settings["mq"].get("host", "localhost"))
75
75
  MQ_PORT = int(os.getenv("MQ_PORT", settings["mq"].get("port", "6379")))
76
-
77
- MQ_BUFFER_SIZE = int(settings["mq"].get("buffer_size", 50))
78
- MQ_INSERTION_BUFFER_TIME = int(settings["mq"].get("insertion_buffer_time_secs", 5))
76
+ MQ_BUFFER_SIZE = settings["mq"].get("buffer_size", None)
77
+ MQ_INSERTION_BUFFER_TIME = settings["mq"].get("insertion_buffer_time_secs", None)
78
+ MQ_TIMING = settings["mq"].get("timing", False)
79
79
  MQ_CHUNK_SIZE = int(settings["mq"].get("chunk_size", -1))
80
80
 
81
81
  #####################
@@ -127,7 +127,7 @@ if not LMDB_ENABLED and not MONGO_ENABLED:
127
127
  ##########################
128
128
  db_buffer_settings = settings["db_buffer"]
129
129
  # In seconds:
130
- INSERTION_BUFFER_TIME = int(db_buffer_settings.get("insertion_buffer_time_secs", 5))
130
+ INSERTION_BUFFER_TIME = db_buffer_settings.get("insertion_buffer_time_secs", None)
131
131
  ADAPTIVE_DB_BUFFER_SIZE = db_buffer_settings.get("adaptive_buffer_size", True)
132
132
  DB_MAX_BUFFER_SIZE = int(db_buffer_settings.get("max_buffer_size", 50))
133
133
  DB_MIN_BUFFER_SIZE = max(1, int(db_buffer_settings.get("min_buffer_size", 10)))
@@ -262,6 +262,7 @@ class Flowcept(object):
262
262
  if not MQDao.build().liveness_test():
263
263
  logger.error("MQ Not Ready!")
264
264
  return False
265
+
265
266
  if MONGO_ENABLED:
266
267
  from flowcept.commons.daos.docdb_dao.mongodb_dao import MongoDBDAO
267
268
 
@@ -21,32 +21,6 @@ from flowcept.flowceptor.adapters.instrumentation_interceptor import Instrumenta
21
21
 
22
22
  def get_run_spec_data(task_msg: TaskObject, run_spec):
23
23
  """Get the run specs."""
24
- # def _get_arg(arg_name):
25
- # if type(run_spec) == dict:
26
- # return run_spec.get(arg_name, None)
27
- # elif hasattr(run_spec, arg_name):
28
- # return getattr(run_spec, arg_name)
29
- # return None
30
- #
31
- # def _parse_dask_tuple(_tuple: tuple):
32
- # forth_elem = None
33
- # if len(_tuple) == 3:
34
- # _, _, value_tuple = _tuple
35
- # elif len(_tuple) == 4:
36
- # _, _, value_tuple, forth_elem = _tuple
37
- #
38
- # _, value = value_tuple
39
- # if len(value) == 1: # Value is always an array here
40
- # value = value[0]
41
- # ret_obj = {"value": value}
42
- #
43
- # if forth_elem is not None and type(forth_elem) == dict:
44
- # ret_obj.update(forth_elem)
45
- # else:
46
- # pass # We don't know yet what to do if this happens. So just pass.
47
- #
48
- # return ret_obj
49
-
50
24
  func = run_spec[0]
51
25
  task_msg.activity_id = func.__name__
52
26
  args = run_spec[1]
@@ -91,7 +65,7 @@ class DaskWorkerInterceptor(BaseInterceptor):
91
65
  self._plugin_key = plugin_key
92
66
  self._worker = None
93
67
  self.kind = kind
94
- # super().__init__ goes to setup_worker.
68
+ # super().__init__ goes to setup_worker. I'm leaving this commented here as a reminder.
95
69
 
96
70
  def setup_worker(self, worker):
97
71
  """Set the worker.
@@ -101,10 +75,13 @@ class DaskWorkerInterceptor(BaseInterceptor):
101
75
  """
102
76
  self._worker = worker
103
77
  super().__init__(plugin_key=self._plugin_key, kind=self.kind)
104
- # TODO: :refactor: Below is just to avoid the auto-generation of workflow id, which doesnt make sense in Dask.
78
+ self._worker.flowcept_tasks = {}
79
+ # TODO: :refactor: Below is just to avoid the auto-generation of workflow id, which doesn't make sense in Dask.
105
80
  self._generated_workflow_id = True
106
81
  super().start(bundle_exec_id=self._worker.scheduler.address)
107
82
 
83
+ self._worker._interceptor = self
84
+
108
85
  instrumentation = INSTRUMENTATION.get("enabled", False)
109
86
  if instrumentation:
110
87
  InstrumentationInterceptor.get_instance().start(
@@ -129,6 +106,8 @@ class DaskWorkerInterceptor(BaseInterceptor):
129
106
  task_msg.address = self._worker.worker_address
130
107
  if self.settings.worker_create_timestamps:
131
108
  task_msg.started_at = get_utc_now()
109
+
110
+ self._worker.flowcept_tasks[task_id] = task_msg
132
111
  elif ts.state == "memory":
133
112
  task_msg.status = Status.FINISHED
134
113
  if self.settings.worker_create_timestamps:
@@ -1,8 +1,11 @@
1
1
  """Dask plugin module."""
2
2
 
3
+ from typing import Optional
4
+
3
5
  from distributed import Client, WorkerPlugin
4
6
 
5
7
  from flowcept import WorkflowObject
8
+ from flowcept.commons.flowcept_dataclasses.task_object import TaskObject
6
9
  from flowcept.configs import INSTRUMENTATION
7
10
  from flowcept.flowceptor.adapters.dask.dask_interceptor import (
8
11
  DaskWorkerInterceptor,
@@ -10,38 +13,6 @@ from flowcept.flowceptor.adapters.dask.dask_interceptor import (
10
13
  from flowcept.flowceptor.adapters.instrumentation_interceptor import InstrumentationInterceptor
11
14
 
12
15
 
13
- # def _set_workflow_on_scheduler(
14
- # dask_scheduler=None,
15
- # workflow_id=None,
16
- # custom_metadata: dict = None,
17
- # campaign_id: str = None,
18
- # workflow_name: str = None,
19
- # used: dict = None,
20
- # ):
21
- # custom_metadata = custom_metadata or {}
22
- # wf_obj = WorkflowObject()
23
- # wf_obj.workflow_id = workflow_id
24
- # custom_metadata.update(
25
- # {
26
- # "workflow_type": "DaskWorkflow",
27
- # "scheduler": dask_scheduler.address_safe,
28
- # "scheduler_id": dask_scheduler.id,
29
- # "scheduler_pid": dask_scheduler.proc.pid,
30
- # "clients": len(dask_scheduler.clients),
31
- # "n_workers": len(dask_scheduler.workers),
32
- # }
33
- # )
34
- # wf_obj.custom_metadata = custom_metadata
35
- # wf_obj.used = used
36
- # wf_obj.campaign_id = campaign_id
37
- # wf_obj.name = workflow_name
38
- #
39
- # interceptor = BaseInterceptor(plugin_key="dask")
40
- # interceptor.start(bundle_exec_id=dask_scheduler.address)
41
- # interceptor.send_workflow_message(wf_obj)
42
- # interceptor.stop()
43
-
44
-
45
16
  def _set_workflow_on_workers(dask_worker, workflow_id, campaign_id=None):
46
17
  setattr(dask_worker, "current_workflow_id", workflow_id)
47
18
  if campaign_id:
@@ -53,6 +24,23 @@ def set_workflow_info_on_workers(dask_client: Client, wf_obj: WorkflowObject):
53
24
  dask_client.run(_set_workflow_on_workers, workflow_id=wf_obj.workflow_id, campaign_id=wf_obj.campaign_id)
54
25
 
55
26
 
27
+ def get_flowcept_task() -> Optional[TaskObject]:
28
+ """Get the Flowcept Task Object inside a Worker's task."""
29
+ from distributed import get_worker
30
+ from distributed.worker import thread_state
31
+
32
+ worker = get_worker()
33
+ try:
34
+ task_id = thread_state.key if hasattr(thread_state, "key") else None
35
+ if hasattr(worker, "flowcept_tasks") and task_id in worker.flowcept_tasks:
36
+ return worker.flowcept_tasks[task_id]
37
+ else:
38
+ return None
39
+ except Exception as e:
40
+ print(e)
41
+ return None
42
+
43
+
56
44
  class FlowceptDaskWorkerAdapter(WorkerPlugin):
57
45
  """Dask worker adapter."""
58
46
 
@@ -45,13 +45,11 @@ def curate_task_msg(task_msg_dict: dict, convert_times=True):
45
45
  task_msg_dict["workflow_id"] = task_msg_dict["used"].pop("workflow_id")
46
46
 
47
47
  if convert_times:
48
- has_time_fields = False
49
48
  for time_field in TaskObject.get_time_field_names():
50
49
  if time_field in task_msg_dict:
51
- has_time_fields = True
52
50
  task_msg_dict[time_field] = datetime.fromtimestamp(task_msg_dict[time_field], pytz.utc)
53
51
 
54
- if not has_time_fields:
52
+ if "registered_at" not in task_msg_dict:
55
53
  task_msg_dict["registered_at"] = datetime.fromtimestamp(time(), pytz.utc)
56
54
 
57
55
 
@@ -71,10 +71,10 @@ class DocumentInserter:
71
71
  self._bundle_exec_id = bundle_exec_id
72
72
  self.check_safe_stops = check_safe_stops
73
73
  self.buffer: AutoflushBuffer = AutoflushBuffer(
74
- max_size=self._curr_max_buffer_size,
75
- flush_interval=INSERTION_BUFFER_TIME,
76
74
  flush_function=DocumentInserter.flush_function,
77
75
  flush_function_kwargs={"logger": self.logger, "doc_daos": self._doc_daos},
76
+ max_size=self._curr_max_buffer_size,
77
+ flush_interval=INSERTION_BUFFER_TIME,
78
78
  )
79
79
 
80
80
  def _set_buffer_size(self):
@@ -119,8 +119,8 @@ class GPUCapture:
119
119
  if "name" in gpu_conf:
120
120
  flowcept_gpu_info["name"] = nvmlDeviceGetName(device)
121
121
 
122
- if "ix" in gpu_conf:
123
- flowcept_gpu_info["gpu_ix"] = gpu_ix
122
+ if "id" in gpu_conf:
123
+ flowcept_gpu_info["id"] = nvmlDeviceGetUUID(device)
124
124
 
125
125
  return flowcept_gpu_info
126
126
 
@@ -160,7 +160,6 @@ class GPUCapture:
160
160
  }
161
161
  if "others" in gpu_conf:
162
162
  flowcept_gpu_info["others"] = {
163
- "uuid": amdsmi_get_gpu_device_uuid(device),
164
163
  "current_gfxclk": all_metrics["current_gfxclk"],
165
164
  "current_socclk": all_metrics["current_socclk"],
166
165
  "current_uclk": all_metrics["current_uclk"],
@@ -168,6 +167,9 @@ class GPUCapture:
168
167
  "current_dclk0": all_metrics["current_dclk0"],
169
168
  }
170
169
 
170
+ if "id" in gpu_conf:
171
+ flowcept_gpu_info["id"] = (amdsmi_get_gpu_device_uuid(device),)
172
+
171
173
  return flowcept_gpu_info
172
174
 
173
175
 
@@ -193,6 +195,7 @@ elif GPUCapture.GPU_VENDOR == "nvidia":
193
195
  nvmlDeviceGetTemperature,
194
196
  nvmlDeviceGetPowerUsage,
195
197
  NVML_TEMPERATURE_GPU,
198
+ nvmlDeviceGetUUID,
196
199
  )
197
200
 
198
201
  FlowceptLogger().debug("Imported Nvidia modules!")
@@ -156,13 +156,13 @@ class FlowceptLoop:
156
156
  "used": {"i": self._next_counter, self._item_name: self._current_item},
157
157
  "parent_task_id": self._parent_task_id,
158
158
  }
159
- tel = FlowceptLoop._interceptor.telemetry_capture.capture()
160
- if tel:
161
- iteration_task["telemetry_at_start"] = tel.to_dict()
162
159
  return iteration_task
163
160
 
164
- def _end_iteration_task(self, iteration_task):
165
- iteration_task["status"] = Status.FINISHED.value
161
+ def _end_iteration_task(self, _):
162
+ self._last_iteration_task["status"] = Status.FINISHED.value
163
+ tel = FlowceptLoop._interceptor.telemetry_capture.capture()
164
+ if tel:
165
+ self._last_iteration_task["telemetry_at_end"] = tel.to_dict()
166
166
  FlowceptLoop._interceptor.intercept(self._last_iteration_task)
167
167
 
168
168
  def _do_nothing_in_end_iter(self, *args, **kwargs):
flowcept/version.py CHANGED
@@ -4,4 +4,4 @@
4
4
  # The expected format is: <Major>.<Minor>.<Patch>
5
5
  # This file is supposed to be automatically modified by the CI Bot.
6
6
  # See .github/workflows/version_bumper.py
7
- __version__ = "0.8.3"
7
+ __version__ = "0.8.5"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: flowcept
3
- Version: 0.8.3
3
+ Version: 0.8.5
4
4
  Summary: Capture and query workflow provenance data using data observability
5
5
  Project-URL: GitHub, https://github.com/ORNL/flowcept
6
6
  Author: Oak Ridge National Laboratory
@@ -1,12 +1,12 @@
1
1
  flowcept/__init__.py,sha256=CukmdzTUvm6Y_plTKPq4kKn7w9LdR36j7V_C_UQyjhU,2011
2
- flowcept/configs.py,sha256=f9lgOouFJW2It0dBKFgmqxna9zy6H3Mq8Tqa8JYFBg4,7471
3
- flowcept/version.py,sha256=Nab3pVvVwFctq6iY0yZ9Rf57aFEh8V4wLrm4yh5C7jU,306
2
+ flowcept/configs.py,sha256=_-jhoI_HGKjzymjYTlDuysbM38Gr2aunc0Q-Stlmcwk,7511
3
+ flowcept/version.py,sha256=RW_aTLB2vWTDjrpIPcWytUXxQhZLynom14B2UHfVVcU,306
4
4
  flowcept/analytics/__init__.py,sha256=46q-7vsHq_ddPNrzNnDgEOiRgvlx-5Ggu2ocyROMV0w,641
5
5
  flowcept/analytics/analytics_utils.py,sha256=FRJdBtQa7Hrk2oR_FFhmhmMf3X6YyZ4nbH5RIYh7KL4,8753
6
6
  flowcept/analytics/data_augmentation.py,sha256=Dyr5x316Zf-k1e8rVoQMCpFOrklYVHjfejRPrtoycmc,1641
7
7
  flowcept/analytics/plot.py,sha256=L56y1HRnTE6-Fxs62Y0rV2OtDwjSwgSP3yLdalkiRBQ,2932
8
8
  flowcept/commons/__init__.py,sha256=W94CqapS0IGuuIGHHaz4sNuuiYhgtJWtpDEbnI0pGwI,26
9
- flowcept/commons/autoflush_buffer.py,sha256=dOwMAyY3mW_GWUFPRNDm3vSDxS3gOdFgH9cgG6Ljqjc,2501
9
+ flowcept/commons/autoflush_buffer.py,sha256=8M0fcIeHck-mSGQ2HFpW3_Af8-dHswhIbUMX5FATm48,2589
10
10
  flowcept/commons/flowcept_logger.py,sha256=0asRucrDMeRXvsdhuCmH6lWO7lAt_Z5o5uW7rrQhcjc,1857
11
11
  flowcept/commons/query_utils.py,sha256=3tyK5VYA10iDtmtzNwa8OQGn93DBxsu6rTjHDphftSc,2208
12
12
  flowcept/commons/settings_factory.py,sha256=vpvnpqabPKK-mzoZ5iLdnefpemW1MldDOZhii_m9wNk,1814
@@ -19,18 +19,18 @@ flowcept/commons/daos/docdb_dao/docdb_dao_base.py,sha256=YbfSVJPwZGK2GBYkeapRC83
19
19
  flowcept/commons/daos/docdb_dao/lmdb_dao.py,sha256=dJOLgCx_lwdz6MKiMpM_UE4rm0angDCPaVz_WU5KqIA,10407
20
20
  flowcept/commons/daos/docdb_dao/mongodb_dao.py,sha256=-Kxjep1FbjKiGjvzyvePVHDf-Q1lOIce1EzBURSKubc,38037
21
21
  flowcept/commons/daos/mq_dao/__init__.py,sha256=Xxm4FmbBUZDQ7XIAmSFbeKE_AdHsbgFmSuftvMWSykQ,21
22
- flowcept/commons/daos/mq_dao/mq_dao_base.py,sha256=SZTwqnYbn70AC6kYQxfvl9AWPyE3iFQuWr23pufHoEc,8077
23
- flowcept/commons/daos/mq_dao/mq_dao_kafka.py,sha256=o8MmhF00XRaLEeCrh8btkXBupD4sLMEH-vRp7bCiYMw,3589
24
- flowcept/commons/daos/mq_dao/mq_dao_mofka.py,sha256=xjlMBnu1kR-WyHbZgM_b7Hrda6rtFPQHZConnVMIAo4,3006
25
- flowcept/commons/daos/mq_dao/mq_dao_redis.py,sha256=9qbRyJjgUkC75VN0lK6XmOZ6H8C9x6bMsSZWyFtH6GI,3304
22
+ flowcept/commons/daos/mq_dao/mq_dao_base.py,sha256=EAqOhy7Q8V29JFDG8C50nRK34KsPxEICkG4elk4ZfX8,9020
23
+ flowcept/commons/daos/mq_dao/mq_dao_kafka.py,sha256=bf-bZvWw9JJk8Kdfzx2UkAnQC95rSrKXDEyYkrcncOk,4400
24
+ flowcept/commons/daos/mq_dao/mq_dao_mofka.py,sha256=aZ810wN5Wkjk7oRUxDWJWOIREUsmq57oI4AxY1bWBuk,3940
25
+ flowcept/commons/daos/mq_dao/mq_dao_redis.py,sha256=Br97SoDIkt4dHH937Yjg3wtkn1xGT-x9t-8E3VD5TeU,4277
26
26
  flowcept/commons/flowcept_dataclasses/__init__.py,sha256=8KkiJh0WSRAB50waVluxCSI8Tb9X1L9nup4c8RN3ulc,30
27
27
  flowcept/commons/flowcept_dataclasses/base_settings_dataclasses.py,sha256=Cjw2PGYtZDfnwecz6G3S42Ncmxj7AIZVEBx05bsxRUo,399
28
28
  flowcept/commons/flowcept_dataclasses/task_object.py,sha256=3DD5ZNMz7EVILS9PRkQ3khboav7lIKoUC5W6sKMFauQ,4694
29
29
  flowcept/commons/flowcept_dataclasses/telemetry.py,sha256=9_5ONCo-06r5nKHXmi5HfIhiZSuPgmTECiq_u9MlxXM,2822
30
- flowcept/commons/flowcept_dataclasses/workflow_object.py,sha256=t9M0eVdcq3NLZgT4fwXrR3x2oOgjtE5Jo5_MAi4-0YM,4283
30
+ flowcept/commons/flowcept_dataclasses/workflow_object.py,sha256=FBpel5PulrN3mCCk3hrQhoYiFqd-4aNV4tT03bCV3DE,4376
31
31
  flowcept/flowcept_api/__init__.py,sha256=T1ty86YlocQ5Z18l5fUqHj_CC6Unq_iBv0lFyiI7Ao8,22
32
32
  flowcept/flowcept_api/db_api.py,sha256=hKXep-n50rp9cAzV0ljk2QVEF8O64yxi3ujXv5_Ibac,9723
33
- flowcept/flowcept_api/flowcept_controller.py,sha256=zq4cRM14xoeRA6HPL6JwLwDLpsVra5ej1EAPmiVKUIs,11932
33
+ flowcept/flowcept_api/flowcept_controller.py,sha256=lkHR7O0zAAfbGtVa4o9tjZMdZquYN7vdnymRKzc4B8s,11933
34
34
  flowcept/flowcept_api/task_query_api.py,sha256=SrwB0OCVtbpvCPECkE2ySM10G_g8Wlk5PJ8h-0xEaNc,23821
35
35
  flowcept/flowcept_webserver/__init__.py,sha256=8411GIXGddKTKoHUvbo_Rq6svosNG7tG8VzvUEBd7WI,28
36
36
  flowcept/flowcept_webserver/app.py,sha256=VUV8_JZbIbx9u_1O7m7XtRdhZb_7uifUa-iNlPhmZws,658
@@ -38,15 +38,15 @@ flowcept/flowcept_webserver/resources/__init__.py,sha256=XOk5yhLeLU6JmVXxbl3TY2z
38
38
  flowcept/flowcept_webserver/resources/query_rsrc.py,sha256=Mk1XDC_wVYkMk0eaazqWWrTC07gQU9U0toKfip0ihZE,1353
39
39
  flowcept/flowcept_webserver/resources/task_messages_rsrc.py,sha256=0u68it2W-9NzUUx5fWOZCqvRKe5EsLI8oyvto9634Ng,666
40
40
  flowcept/flowceptor/__init__.py,sha256=wVxRXUv07iNx6SMRRma2vqhR_GIcRl0re_WCYG65PUs,29
41
- flowcept/flowceptor/telemetry_capture.py,sha256=8LK4o3OaZD8B6KTpgpvD9D834dFpAJpX-NN2bc91jZU,13658
41
+ flowcept/flowceptor/telemetry_capture.py,sha256=FlWGLpzb6pBJOKVi349kyZKzAHeTsL86BCQd4Wtxpig,13746
42
42
  flowcept/flowceptor/adapters/__init__.py,sha256=SuZbSZVVQeBJ9zXW-M9jF09dw3XIjre3lSGrUO1Y8Po,27
43
43
  flowcept/flowceptor/adapters/base_interceptor.py,sha256=99a_Ipnj6g8qZMHWLBEYJh0Cox033ADxOKPFrivr9gw,6056
44
44
  flowcept/flowceptor/adapters/instrumentation_interceptor.py,sha256=DhK2bBnpghqPSeA62BUqRg6pl8zxuYrP33dK4x6PhRE,733
45
45
  flowcept/flowceptor/adapters/interceptor_state_manager.py,sha256=xRzmi5YFKBEqNtX8F5s6XlMTRe27ml4BmQtBO4WtG2c,919
46
46
  flowcept/flowceptor/adapters/dask/__init__.py,sha256=GKreb5L_nliD2BEckyB943zOQ-b6Gn1fLDj81FqSK2Y,23
47
47
  flowcept/flowceptor/adapters/dask/dask_dataclasses.py,sha256=6LTG-kdcc6AUuVINvkqB5QHw6pchg1aMqj0sdWt2Ef8,580
48
- flowcept/flowceptor/adapters/dask/dask_interceptor.py,sha256=vHEUpp2LusOieQq4V3qTXK3_FpGaaevCCs9jiw-3xaA,6554
49
- flowcept/flowceptor/adapters/dask/dask_plugins.py,sha256=rIrG8CCaggJRZ3hRxu-1cpCcCyAbkThDzGBzr2dIsDc,2874
48
+ flowcept/flowceptor/adapters/dask/dask_interceptor.py,sha256=uBQpLluYXzlT1gBDfTe4_WueC_fWBEs5Xr8ntpOmljE,5869
49
+ flowcept/flowceptor/adapters/dask/dask_plugins.py,sha256=s1ENAi9N61PC_6RiFvOYhJsgWzSm_lFWm3w87V-R1YY,2473
50
50
  flowcept/flowceptor/adapters/mlflow/__init__.py,sha256=3mzHrvh1XQOy68qx1A3so9Nq27tIb0i2mSXfv3F6gZg,25
51
51
  flowcept/flowceptor/adapters/mlflow/interception_event_handler.py,sha256=-SsIRdOcZjQUTzWgsZ41ouqpla4Qd32jIWXIAGU1pPw,494
52
52
  flowcept/flowceptor/adapters/mlflow/mlflow_dao.py,sha256=dPEgCduiw14_pzT5WCjuokwaN7p5Tu7UvWS2rtGh4qk,4589
@@ -59,15 +59,15 @@ flowcept/flowceptor/adapters/zambeze/__init__.py,sha256=1e9_hK2cUKDXhQ0kBRftwcJj
59
59
  flowcept/flowceptor/adapters/zambeze/zambeze_dataclasses.py,sha256=nn9MxvcdzgmOa8n5Jwdl7UzlSzxEu9bA-Ls6cHyb91c,849
60
60
  flowcept/flowceptor/adapters/zambeze/zambeze_interceptor.py,sha256=Bjyi48JW0DXJLJuvwPxaD8zxxsSoEFgSoXl8YcbwFWk,3782
61
61
  flowcept/flowceptor/consumers/__init__.py,sha256=foxtVEb2ZEe9g1slfYIKM4tIFv-He1l7XS--SYs7nlQ,28
62
- flowcept/flowceptor/consumers/consumer_utils.py,sha256=JmyjQeZPqMj_yqFlxxw9k2_JZvZkAmX7kySV__YvEVc,5719
63
- flowcept/flowceptor/consumers/document_inserter.py,sha256=od1M3A_N74nIXociWRHAajPLLCWtJdfI8JgNQ9eSPf8,9339
62
+ flowcept/flowceptor/consumers/consumer_utils.py,sha256=7bvFJWusJkfA4j0gwZLDIIsIOyfk9wRq6s5liS3JAV0,5665
63
+ flowcept/flowceptor/consumers/document_inserter.py,sha256=rAK3rs3VNW5a6koesE05scQ1mR_4BhuxLurP10ipURs,9339
64
64
  flowcept/instrumentation/__init__.py,sha256=M5bTmg80E4QyN91gUX3qfw_nbtJSXwGWcKxdZP3vJz0,34
65
- flowcept/instrumentation/flowcept_loop.py,sha256=9Ap7-PfpNdwS7DaRDaB-R9G3X_G3RZvGVkNVUZAix5A,12164
65
+ flowcept/instrumentation/flowcept_loop.py,sha256=RvETm3Pn37dIw_a1RXigyh2U7MCBHqi46dPmbrz3RMQ,12171
66
66
  flowcept/instrumentation/flowcept_task.py,sha256=l_BAYEUZ_SeBt8QJN_E9D9QcZVYRnW9qO_XRnqvmePE,5993
67
67
  flowcept/instrumentation/flowcept_torch.py,sha256=KXA1HBwz8l5Qp7PkZ7nsbYlM8IcwWD_u04NxaAcZPzM,23395
68
68
  flowcept/instrumentation/task_capture.py,sha256=u82r_SgzoVKyb6_SWtfB-meBUZgjrXvF5dxkH9vnMDs,4776
69
- resources/sample_settings.yaml,sha256=fRWyuIXizQUjzChIL8ze5zy5bwFgETNZCByJ35NWZb4,3408
70
- flowcept-0.8.3.dist-info/METADATA,sha256=jgA-zJnRY-cbB1b70nEdtXs3kToTLdt41gi9cJVbqQo,17543
71
- flowcept-0.8.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
72
- flowcept-0.8.3.dist-info/licenses/LICENSE,sha256=r5-2P6tFTuRGWT5TiX32s1y0tnp4cIqBEC1QjTaXe2k,1086
73
- flowcept-0.8.3.dist-info/RECORD,,
69
+ resources/sample_settings.yaml,sha256=aZRAZRkgCe52i-8czQvZsEIAz8dGau-OF2YClUF3QGs,3427
70
+ flowcept-0.8.5.dist-info/METADATA,sha256=uZzz3Hz-Pee3zpeyNYLk5K-JbS_miCTMXIXlqtMBXX0,17543
71
+ flowcept-0.8.5.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
72
+ flowcept-0.8.5.dist-info/licenses/LICENSE,sha256=r5-2P6tFTuRGWT5TiX32s1y0tnp4cIqBEC1QjTaXe2k,1086
73
+ flowcept-0.8.5.dist-info/RECORD,,
@@ -13,7 +13,7 @@ log:
13
13
  log_stream_level: error
14
14
 
15
15
  telemetry_capture:
16
- gpu: ~ # ~ means None. This is a list with GPU metrics. AMD=[activity,used,power,temperature,others]; NVIDIA=[used,temperature,power,name,ix]
16
+ gpu: ~ # ~ means None. This is a list with GPU metrics. AMD=[activity,used,power,temperature,others,id]; NVIDIA=[used,temperature,power,name,id]
17
17
  cpu: true
18
18
  per_cpu: true
19
19
  process_info: true
@@ -44,6 +44,7 @@ mq:
44
44
  channel: interception
45
45
  buffer_size: 50
46
46
  insertion_buffer_time_secs: 5
47
+ timing: false
47
48
  chunk_size: -1 # use 0 or -1 to disable this. Or simply omit this from the config file.
48
49
 
49
50
  kv_db: