cognite-extractor-utils 7.5.7__py3-none-any.whl → 7.5.9__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.

Potentially problematic release.


This version of cognite-extractor-utils might be problematic. Click here for more details.

Files changed (38) hide show
  1. cognite/extractorutils/__init__.py +1 -1
  2. cognite/extractorutils/_inner_util.py +1 -1
  3. cognite/extractorutils/base.py +4 -3
  4. cognite/extractorutils/configtools/__init__.py +1 -1
  5. cognite/extractorutils/configtools/_util.py +2 -1
  6. cognite/extractorutils/configtools/elements.py +1 -1
  7. cognite/extractorutils/configtools/loaders.py +10 -9
  8. cognite/extractorutils/exceptions.py +1 -1
  9. cognite/extractorutils/metrics.py +7 -6
  10. cognite/extractorutils/statestore/hashing.py +6 -6
  11. cognite/extractorutils/statestore/watermark.py +13 -13
  12. cognite/extractorutils/threading.py +1 -1
  13. cognite/extractorutils/unstable/configuration/exceptions.py +2 -5
  14. cognite/extractorutils/unstable/configuration/loaders.py +8 -8
  15. cognite/extractorutils/unstable/configuration/models.py +12 -12
  16. cognite/extractorutils/unstable/core/base.py +55 -50
  17. cognite/extractorutils/unstable/core/errors.py +18 -3
  18. cognite/extractorutils/unstable/core/logger.py +149 -0
  19. cognite/extractorutils/unstable/core/restart_policy.py +1 -1
  20. cognite/extractorutils/unstable/core/runtime.py +10 -55
  21. cognite/extractorutils/unstable/core/tasks.py +99 -14
  22. cognite/extractorutils/unstable/scheduling/_scheduler.py +2 -2
  23. cognite/extractorutils/uploader/_base.py +2 -1
  24. cognite/extractorutils/uploader/assets.py +3 -2
  25. cognite/extractorutils/uploader/data_modeling.py +3 -2
  26. cognite/extractorutils/uploader/events.py +2 -2
  27. cognite/extractorutils/uploader/files.py +13 -18
  28. cognite/extractorutils/uploader/raw.py +3 -2
  29. cognite/extractorutils/uploader/time_series.py +9 -8
  30. cognite/extractorutils/uploader/upload_failure_handler.py +2 -2
  31. cognite/extractorutils/uploader_extractor.py +7 -6
  32. cognite/extractorutils/uploader_types.py +2 -1
  33. cognite/extractorutils/util.py +9 -8
  34. {cognite_extractor_utils-7.5.7.dist-info → cognite_extractor_utils-7.5.9.dist-info}/METADATA +30 -36
  35. cognite_extractor_utils-7.5.9.dist-info/RECORD +50 -0
  36. {cognite_extractor_utils-7.5.7.dist-info → cognite_extractor_utils-7.5.9.dist-info}/WHEEL +1 -1
  37. cognite_extractor_utils-7.5.7.dist-info/RECORD +0 -49
  38. {cognite_extractor_utils-7.5.7.dist-info → cognite_extractor_utils-7.5.9.dist-info/licenses}/LICENSE +0 -0
@@ -2,13 +2,12 @@ import logging
2
2
  import logging.config
3
3
  import time
4
4
  from concurrent.futures import ThreadPoolExecutor
5
- from contextvars import ContextVar, Token
5
+ from functools import partial
6
6
  from logging.handlers import TimedRotatingFileHandler
7
7
  from multiprocessing import Queue
8
8
  from threading import RLock, Thread
9
- from traceback import format_exception
10
9
  from types import TracebackType
11
- from typing import Generic, Literal, Type, TypeVar
10
+ from typing import Generic, Literal, TypeVar
12
11
 
13
12
  from humps import pascalize
14
13
  from typing_extensions import Self, assert_never
@@ -25,8 +24,9 @@ from cognite.extractorutils.unstable.core._dto import Error as DtoError
25
24
  from cognite.extractorutils.unstable.core._dto import TaskUpdate
26
25
  from cognite.extractorutils.unstable.core._messaging import RuntimeMessage
27
26
  from cognite.extractorutils.unstable.core.errors import Error, ErrorLevel
27
+ from cognite.extractorutils.unstable.core.logger import CogniteLogger
28
28
  from cognite.extractorutils.unstable.core.restart_policy import WHEN_CONTINUOUS_TASKS_CRASHES, RestartPolicy
29
- from cognite.extractorutils.unstable.core.tasks import ContinuousTask, ScheduledTask, StartupTask, Task
29
+ from cognite.extractorutils.unstable.core.tasks import ContinuousTask, ScheduledTask, StartupTask, Task, TaskContext
30
30
  from cognite.extractorutils.unstable.scheduling import TaskScheduler
31
31
  from cognite.extractorutils.util import now
32
32
 
@@ -45,32 +45,31 @@ class FullConfig(Generic[_T]):
45
45
  connection_config: ConnectionConfig,
46
46
  application_config: _T,
47
47
  current_config_revision: ConfigRevision,
48
- newest_config_revision: ConfigRevision,
49
48
  ) -> None:
50
49
  self.connection_config = connection_config
51
50
  self.application_config = application_config
52
51
  self.current_config_revision = current_config_revision
53
- self.newest_config_revision = newest_config_revision
54
52
 
55
53
 
56
- class Extractor(Generic[ConfigType]):
54
+ class Extractor(Generic[ConfigType], CogniteLogger):
57
55
  NAME: str
58
56
  EXTERNAL_ID: str
59
57
  DESCRIPTION: str
60
58
  VERSION: str
61
59
 
62
- CONFIG_TYPE: Type[ConfigType]
60
+ CONFIG_TYPE: type[ConfigType]
63
61
 
64
62
  RESTART_POLICY: RestartPolicy = WHEN_CONTINUOUS_TASKS_CRASHES
65
63
 
66
64
  def __init__(self, config: FullConfig[ConfigType]) -> None:
65
+ self._logger = logging.getLogger(f"{self.EXTERNAL_ID}.main")
66
+
67
67
  self.cancellation_token = CancellationToken()
68
68
  self.cancellation_token.cancel_on_interrupt()
69
69
 
70
70
  self.connection_config = config.connection_config
71
71
  self.application_config = config.application_config
72
72
  self.current_config_revision = config.current_config_revision
73
- self.newest_config_revision = config.newest_config_revision
74
73
 
75
74
  self.cognite_client = self.connection_config.get_cognite_client(f"{self.EXTERNAL_ID}-{self.VERSION}")
76
75
 
@@ -83,10 +82,6 @@ class Extractor(Generic[ConfigType]):
83
82
  self._task_updates: list[TaskUpdate] = []
84
83
  self._errors: dict[str, Error] = {}
85
84
 
86
- self.logger = logging.getLogger(f"{self.EXTERNAL_ID}.main")
87
-
88
- self._current_task: ContextVar[str | None] = ContextVar("current_task", default=None)
89
-
90
85
  self.__init_tasks__()
91
86
 
92
87
  def _setup_logging(self) -> None:
@@ -158,7 +153,7 @@ class Extractor(Generic[ConfigType]):
158
153
  self._errors.clear()
159
154
 
160
155
  res = self.cognite_client.post(
161
- f"/api/v1/projects/{self.cognite_client.config.project}/odin/checkin",
156
+ f"/api/v1/projects/{self.cognite_client.config.project}/integrations/checkin",
162
157
  json={
163
158
  "externalId": self.connection_config.integration,
164
159
  "taskEvents": task_updates,
@@ -171,43 +166,41 @@ class Extractor(Generic[ConfigType]):
171
166
  if (
172
167
  new_config_revision
173
168
  and self.current_config_revision != "local"
174
- and new_config_revision > self.newest_config_revision
169
+ and new_config_revision > self.current_config_revision
175
170
  ):
176
171
  self.restart()
177
172
 
178
173
  def _run_checkin(self) -> None:
179
174
  while not self.cancellation_token.is_cancelled:
180
175
  try:
181
- self.logger.debug("Running checkin")
176
+ self._logger.debug("Running checkin")
182
177
  self._checkin()
183
178
  except Exception:
184
- self.logger.exception("Error during checkin")
179
+ self._logger.exception("Error during checkin")
185
180
  self.cancellation_token.wait(10)
186
181
 
187
182
  def _report_error(self, error: Error) -> None:
188
183
  with self._checkin_lock:
189
184
  self._errors[error.external_id] = error
190
185
 
191
- def error(
186
+ def _new_error(
192
187
  self,
193
188
  level: ErrorLevel,
194
189
  description: str,
195
- details: str | None = None,
196
190
  *,
197
- force_global: bool = False,
191
+ details: str | None = None,
192
+ task_name: str | None = None,
198
193
  ) -> Error:
199
- task_name = self._current_task.get()
200
-
201
194
  return Error(
202
195
  level=level,
203
196
  description=description,
204
197
  details=details,
205
198
  extractor=self,
206
- task_name=None if force_global else task_name,
199
+ task_name=task_name,
207
200
  )
208
201
 
209
202
  def restart(self) -> None:
210
- self.logger.info("Restarting extractor")
203
+ self._logger.info("Restarting extractor")
211
204
  if self._runtime_messages:
212
205
  self._runtime_messages.put(RuntimeMessage.RESTART)
213
206
  self.cancellation_token.cancel()
@@ -220,7 +213,7 @@ class Extractor(Generic[ConfigType]):
220
213
  # Store this for later, since we'll override it with the wrapped version
221
214
  target = task.target
222
215
 
223
- def run_task() -> None:
216
+ def run_task(task_context: TaskContext) -> None:
224
217
  """
225
218
  A wrapped version of the task's target, with tracking and error handling
226
219
  """
@@ -230,33 +223,22 @@ class Extractor(Generic[ConfigType]):
230
223
  TaskUpdate(type="started", name=task.name, timestamp=now()),
231
224
  )
232
225
 
233
- context_token: Token[str | None] | None = None
234
-
235
226
  try:
236
- # Set the current task context var, used to track that we're in a task for error reporting
237
- context_token = self._current_task.set(task.name)
238
-
239
227
  # Run task
240
- target()
228
+ target(task_context)
241
229
 
242
230
  except Exception as e:
243
- self.logger.exception(f"Unexpected error in {task.name}")
244
-
245
231
  # Task crashed, record it as a fatal error
246
- self.error(
247
- ErrorLevel.fatal,
248
- description="Task crashed unexpectedly",
249
- details="".join(format_exception(e)),
250
- ).instant()
232
+ task_context.exception(
233
+ f"Task {task.name} crashed unexpectedly",
234
+ e,
235
+ level=ErrorLevel.fatal,
236
+ )
251
237
 
252
238
  if self.__class__.RESTART_POLICY(task, e):
253
239
  self.restart()
254
240
 
255
241
  finally:
256
- # Unset the current task
257
- if context_token is not None:
258
- self._current_task.reset(context_token)
259
-
260
242
  # Record task end
261
243
  with self._checkin_lock:
262
244
  self._task_updates.append(
@@ -268,11 +250,20 @@ class Extractor(Generic[ConfigType]):
268
250
 
269
251
  match task:
270
252
  case ScheduledTask() as t:
271
- self._scheduler.schedule_task(name=t.name, schedule=t.schedule, task=t.target)
253
+ self._scheduler.schedule_task(
254
+ name=t.name,
255
+ schedule=t.schedule,
256
+ task=lambda: t.target(
257
+ TaskContext(
258
+ task=task,
259
+ extractor=self,
260
+ )
261
+ ),
262
+ )
272
263
 
273
264
  def _report_extractor_info(self) -> None:
274
265
  self.cognite_client.post(
275
- f"/api/v1/projects/{self.cognite_client.config.project}/odin/extractorinfo",
266
+ f"/api/v1/projects/{self.cognite_client.config.project}/integrations/extractorinfo",
276
267
  json={
277
268
  "externalId": self.connection_config.integration,
278
269
  "activeConfigRevision": self.current_config_revision,
@@ -284,6 +275,8 @@ class Extractor(Generic[ConfigType]):
284
275
  {
285
276
  "name": t.name,
286
277
  "type": "continuous" if isinstance(t, ContinuousTask) else "batch",
278
+ "action": True if isinstance(t, ScheduledTask) else False,
279
+ "description": t.description,
287
280
  }
288
281
  for t in self._tasks
289
282
  ],
@@ -305,7 +298,7 @@ class Extractor(Generic[ConfigType]):
305
298
 
306
299
  def __exit__(
307
300
  self,
308
- exc_type: Type[BaseException] | None,
301
+ exc_type: type[BaseException] | None,
309
302
  exc_val: BaseException | None,
310
303
  exc_tb: TracebackType | None,
311
304
  ) -> bool:
@@ -313,7 +306,7 @@ class Extractor(Generic[ConfigType]):
313
306
  with self._checkin_lock:
314
307
  self._checkin()
315
308
 
316
- self.logger.info("Shutting down extractor")
309
+ self._logger.info("Shutting down extractor")
317
310
  return exc_val is None
318
311
 
319
312
  def run(self) -> None:
@@ -336,15 +329,27 @@ class Extractor(Generic[ConfigType]):
336
329
  case _:
337
330
  assert_never(task)
338
331
 
339
- self.logger.info("Starting extractor")
332
+ self._logger.info("Starting extractor")
340
333
  if startup:
341
334
  with ThreadPoolExecutor() as pool:
342
335
  for task in startup:
343
- pool.submit(task.target)
344
- self.logger.info("Startup done")
336
+ pool.submit(
337
+ partial(
338
+ task.target,
339
+ TaskContext(
340
+ task=task,
341
+ extractor=self,
342
+ ),
343
+ )
344
+ )
345
+ self._logger.info("Startup done")
345
346
 
346
347
  for task in continuous:
347
- Thread(name=pascalize(task.name), target=task.target).start()
348
+ Thread(
349
+ name=pascalize(task.name),
350
+ target=task.target,
351
+ args=(TaskContext(task=task, extractor=self),),
352
+ ).start()
348
353
 
349
354
  if has_scheduled:
350
355
  self._scheduler.run()
@@ -1,11 +1,14 @@
1
- import typing
1
+ import logging
2
2
  from enum import Enum
3
3
  from types import TracebackType
4
+ from typing import TYPE_CHECKING
4
5
  from uuid import uuid4
5
6
 
7
+ from typing_extensions import assert_never
8
+
6
9
  from cognite.extractorutils.util import now
7
10
 
8
- if typing.TYPE_CHECKING:
11
+ if TYPE_CHECKING:
9
12
  from .base import Extractor
10
13
 
11
14
  __all__ = ["Error", "ErrorLevel"]
@@ -16,6 +19,18 @@ class ErrorLevel(Enum):
16
19
  error = "error"
17
20
  fatal = "fatal"
18
21
 
22
+ @property
23
+ def log_level(self) -> int:
24
+ match self:
25
+ case ErrorLevel.warning:
26
+ return logging.WARNING
27
+ case ErrorLevel.error:
28
+ return logging.ERROR
29
+ case ErrorLevel.fatal:
30
+ return logging.CRITICAL
31
+ case _:
32
+ assert_never(self)
33
+
19
34
 
20
35
  class Error:
21
36
  def __init__(
@@ -64,7 +79,7 @@ class Error:
64
79
 
65
80
  def __exit__(
66
81
  self,
67
- exc_type: typing.Type[BaseException] | None,
82
+ exc_type: type[BaseException] | None,
68
83
  exc_val: BaseException | None,
69
84
  exc_tb: TracebackType | None,
70
85
  ) -> bool:
@@ -0,0 +1,149 @@
1
+ from abc import ABC, abstractmethod
2
+ from logging import Logger, getLogger
3
+ from traceback import format_exception
4
+ from typing import Literal
5
+
6
+ from typing_extensions import assert_never
7
+
8
+ from cognite.extractorutils.unstable.core.errors import Error, ErrorLevel
9
+
10
+
11
+ class CogniteLogger(ABC):
12
+ def __init__(self) -> None:
13
+ self._logger: Logger = getLogger()
14
+
15
+ @abstractmethod
16
+ def _new_error(
17
+ self,
18
+ level: ErrorLevel,
19
+ description: str,
20
+ *,
21
+ details: str | None = None,
22
+ task_name: str | None = None,
23
+ ) -> Error:
24
+ pass
25
+
26
+ def debug(self, message: str) -> None:
27
+ self._logger.debug(message)
28
+
29
+ def info(self, message: str) -> None:
30
+ self._logger.info(message)
31
+
32
+ def begin_warning(
33
+ self,
34
+ message: str,
35
+ *,
36
+ details: str | None = None,
37
+ auto_log: bool = True,
38
+ ) -> Error:
39
+ if auto_log:
40
+ self._logger.warning(message)
41
+ return self._new_error(
42
+ level=ErrorLevel.warning,
43
+ description=message,
44
+ details=details,
45
+ )
46
+
47
+ def begin_error(
48
+ self,
49
+ message: str,
50
+ *,
51
+ details: str | None = None,
52
+ auto_log: bool = True,
53
+ ) -> Error:
54
+ if auto_log:
55
+ self._logger.error(message)
56
+ return self._new_error(
57
+ level=ErrorLevel.error,
58
+ description=message,
59
+ details=details,
60
+ )
61
+
62
+ def begin_fatal(
63
+ self,
64
+ message: str,
65
+ *,
66
+ details: str | None = None,
67
+ auto_log: bool = True,
68
+ ) -> Error:
69
+ if auto_log:
70
+ self._logger.critical(message)
71
+ return self._new_error(
72
+ level=ErrorLevel.fatal,
73
+ description=message,
74
+ details=details,
75
+ )
76
+
77
+ def warning(
78
+ self,
79
+ message: str,
80
+ *,
81
+ details: str | None = None,
82
+ auto_log: bool = True,
83
+ ) -> None:
84
+ if auto_log:
85
+ self._logger.warning(message)
86
+ self._new_error(
87
+ level=ErrorLevel.warning,
88
+ description=message,
89
+ details=details,
90
+ ).instant()
91
+
92
+ def error(
93
+ self,
94
+ message: str,
95
+ *,
96
+ details: str | None = None,
97
+ auto_log: bool = True,
98
+ ) -> None:
99
+ if auto_log:
100
+ self._logger.error(message)
101
+ self._new_error(
102
+ level=ErrorLevel.error,
103
+ description=message,
104
+ details=details,
105
+ ).instant()
106
+
107
+ def fatal(
108
+ self,
109
+ message: str,
110
+ *,
111
+ details: str | None = None,
112
+ auto_log: bool = True,
113
+ ) -> None:
114
+ if auto_log:
115
+ self._logger.critical(message)
116
+ self._new_error(
117
+ level=ErrorLevel.fatal,
118
+ description=message,
119
+ details=details,
120
+ ).instant()
121
+
122
+ def exception(
123
+ self,
124
+ message: str,
125
+ exception: Exception,
126
+ *,
127
+ level: ErrorLevel = ErrorLevel.error,
128
+ include_details: Literal["stack_trace"] | Literal["exception_message"] | bool = "exception_message",
129
+ auto_log: bool = True,
130
+ ) -> None:
131
+ if auto_log:
132
+ self._logger.log(level=level.log_level, msg=message, exc_info=exception)
133
+
134
+ details: str | None
135
+ match include_details:
136
+ case "stack_trace":
137
+ details = "".join(format_exception(exception))
138
+ case "exception_message" | True:
139
+ details = str(exception)
140
+ case False:
141
+ details = None
142
+ case _:
143
+ assert_never(include_details)
144
+
145
+ self._new_error(
146
+ level=level,
147
+ description=message,
148
+ details=details,
149
+ ).instant()
@@ -1,4 +1,4 @@
1
- from typing import Callable
1
+ from collections.abc import Callable
2
2
 
3
3
  from cognite.extractorutils.unstable.core.tasks import ContinuousTask, Task
4
4
 
@@ -5,8 +5,7 @@ import time
5
5
  from argparse import ArgumentParser, Namespace
6
6
  from multiprocessing import Process, Queue
7
7
  from pathlib import Path
8
- from typing import Any, Generic, Type, TypeVar
9
- from uuid import uuid4
8
+ from typing import Any, Generic, TypeVar
10
9
 
11
10
  from requests.exceptions import ConnectionError
12
11
  from typing_extensions import assert_never
@@ -17,7 +16,6 @@ from cognite.extractorutils.unstable.configuration.exceptions import InvalidConf
17
16
  from cognite.extractorutils.unstable.configuration.loaders import load_file, load_from_cdf
18
17
  from cognite.extractorutils.unstable.configuration.models import ConnectionConfig
19
18
  from cognite.extractorutils.unstable.core._dto import Error
20
- from cognite.extractorutils.util import now
21
19
 
22
20
  from ._messaging import RuntimeMessage
23
21
  from .base import ConfigRevision, ConfigType, Extractor, FullConfig
@@ -30,7 +28,7 @@ ExtractorType = TypeVar("ExtractorType", bound=Extractor)
30
28
  class Runtime(Generic[ExtractorType]):
31
29
  def __init__(
32
30
  self,
33
- extractor: Type[ExtractorType],
31
+ extractor: type[ExtractorType],
34
32
  ) -> None:
35
33
  self._extractor_class = extractor
36
34
  self._cancellation_token = CancellationToken()
@@ -127,15 +125,13 @@ class Runtime(Generic[ExtractorType]):
127
125
  self,
128
126
  args: Namespace,
129
127
  connection_config: ConnectionConfig,
130
- ) -> tuple[ConfigType, ConfigRevision, ConfigRevision]:
128
+ ) -> tuple[ConfigType, ConfigRevision]:
131
129
  current_config_revision: ConfigRevision
132
- newest_config_revision: ConfigRevision
133
130
 
134
131
  if args.local_override:
135
132
  self.logger.info("Loading local application config")
136
133
 
137
134
  current_config_revision = "local"
138
- newest_config_revision = "local"
139
135
  try:
140
136
  application_config = load_file(args.local_override[0], self._extractor_class.CONFIG_TYPE)
141
137
  except InvalidConfigError as e:
@@ -153,50 +149,12 @@ class Runtime(Generic[ExtractorType]):
153
149
 
154
150
  errors: list[Error] = []
155
151
 
156
- revision: int | None = None
157
152
  try:
158
- while True:
159
- try:
160
- application_config, current_config_revision = load_from_cdf(
161
- client,
162
- connection_config.integration,
163
- self._extractor_class.CONFIG_TYPE,
164
- revision=revision,
165
- )
166
- break
167
-
168
- except InvalidConfigError as e:
169
- if e.attempted_revision is None:
170
- # Should never happen, attempted_revision is set in every handler in load_from_cdf, but it's
171
- # needed for type checks to pass
172
- raise e
173
-
174
- self.logger.error(f"Revision {e.attempted_revision} is invalid: {e.message}")
175
-
176
- t = now()
177
- errors.append(
178
- Error(
179
- external_id=str(uuid4()),
180
- level="error",
181
- description=f"Revision {e.attempted_revision} is invalid",
182
- details=e.message,
183
- start_time=t,
184
- end_time=t,
185
- task=None,
186
- )
187
- )
188
-
189
- if revision is None:
190
- revision = e.attempted_revision - 1
191
- newest_config_revision = e.attempted_revision
192
- else:
193
- revision -= 1
194
-
195
- if revision > 0:
196
- self.logger.info(f"Falling back to revision {revision}")
197
- else:
198
- self.logger.critical("No more revisions to fall back to")
199
- raise e
153
+ application_config, current_config_revision = load_from_cdf(
154
+ client,
155
+ connection_config.integration,
156
+ self._extractor_class.CONFIG_TYPE,
157
+ )
200
158
 
201
159
  finally:
202
160
  if errors:
@@ -209,7 +167,7 @@ class Runtime(Generic[ExtractorType]):
209
167
  headers={"cdf-version": "alpha"},
210
168
  )
211
169
 
212
- return application_config, current_config_revision, newest_config_revision
170
+ return application_config, current_config_revision
213
171
 
214
172
  def _verify_connection_config(self, connection_config: ConnectionConfig) -> bool:
215
173
  client = connection_config.get_cognite_client(
@@ -281,9 +239,7 @@ class Runtime(Generic[ExtractorType]):
281
239
  application_config: Any
282
240
  while not self._cancellation_token.is_cancelled:
283
241
  try:
284
- application_config, current_config_revision, newest_config_revision = self._get_application_config(
285
- args, connection_config
286
- )
242
+ application_config, current_config_revision = self._get_application_config(args, connection_config)
287
243
 
288
244
  except InvalidConfigError:
289
245
  self.logger.critical("Could not get a valid application config file. Shutting down")
@@ -295,7 +251,6 @@ class Runtime(Generic[ExtractorType]):
295
251
  connection_config=connection_config,
296
252
  application_config=application_config,
297
253
  current_config_revision=current_config_revision,
298
- newest_config_revision=newest_config_revision,
299
254
  )
300
255
  )
301
256
  process.join()