feldera 0.68.0__py3-none-any.whl → 0.69.0__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 feldera might be problematic. Click here for more details.

feldera/enums.py CHANGED
@@ -237,3 +237,24 @@ class ProgramStatus(Enum):
237
237
  """
238
238
 
239
239
  return self.error
240
+
241
+
242
+ class CheckpointStatus(Enum):
243
+ Success = 1
244
+ Failure = 2
245
+ InProgress = 3
246
+ Unknown = 4
247
+
248
+ def __init__(self, value):
249
+ self.error: Optional[str] = None
250
+ self._value_ = value
251
+
252
+ def __eq__(self, other):
253
+ return self.value == other.value
254
+
255
+ def get_error(self) -> Optional[str]:
256
+ """
257
+ Returns the error, if any.
258
+ """
259
+
260
+ return self.error
feldera/pipeline.py CHANGED
@@ -9,7 +9,7 @@ from collections import deque
9
9
  from queue import Queue
10
10
 
11
11
  from feldera.rest.errors import FelderaAPIError
12
- from feldera.enums import PipelineStatus, ProgramStatus
12
+ from feldera.enums import PipelineStatus, ProgramStatus, CheckpointStatus
13
13
  from feldera.rest.pipeline import Pipeline as InnerPipeline
14
14
  from feldera.rest.feldera_client import FelderaClient
15
15
  from feldera._callback_runner import _CallbackRunnerInstruction, CallbackRunner
@@ -104,7 +104,9 @@ class Pipeline:
104
104
  tbl.name.lower() for tbl in pipeline.tables
105
105
  ]:
106
106
  raise ValueError(
107
- f"Cannot push to table '{table_name}': table with this name does not exist in the '{self.name}' pipeline"
107
+ f"Cannot push to table '{
108
+ table_name
109
+ }': table with this name does not exist in the '{self.name}' pipeline"
108
110
  )
109
111
  else:
110
112
  # consider validating the schema here
@@ -297,10 +299,14 @@ class Pipeline:
297
299
  elapsed = time.monotonic() - start_time
298
300
  if elapsed > timeout_s:
299
301
  raise TimeoutError(
300
- f"timeout ({timeout_s}s) reached while waiting for pipeline '{self.name}' to complete"
302
+ f"timeout ({timeout_s}s) reached while waiting for pipeline '{
303
+ self.name
304
+ }' to complete"
301
305
  )
302
306
  logging.debug(
303
- f"waiting for pipeline {self.name} to complete: elapsed time {elapsed}s, timeout: {timeout_s}s"
307
+ f"waiting for pipeline {self.name} to complete: elapsed time {
308
+ elapsed
309
+ }s, timeout: {timeout_s}s"
304
310
  )
305
311
 
306
312
  metrics: dict = self.client.get_pipeline_stats(self.name).get(
@@ -407,11 +413,15 @@ resume a paused pipeline."""
407
413
  """
408
414
  if idle_interval_s > timeout_s:
409
415
  raise ValueError(
410
- f"idle interval ({idle_interval_s}s) cannot be larger than timeout ({timeout_s}s)"
416
+ f"idle interval ({idle_interval_s}s) cannot be larger than timeout ({
417
+ timeout_s
418
+ }s)"
411
419
  )
412
420
  if poll_interval_s > timeout_s:
413
421
  raise ValueError(
414
- f"poll interval ({poll_interval_s}s) cannot be larger than timeout ({timeout_s}s)"
422
+ f"poll interval ({poll_interval_s}s) cannot be larger than timeout ({
423
+ timeout_s
424
+ }s)"
415
425
  )
416
426
  if poll_interval_s > idle_interval_s:
417
427
  raise ValueError(
@@ -547,15 +557,121 @@ resume a paused pipeline."""
547
557
  if err.status_code == 404:
548
558
  raise RuntimeError(f"Pipeline with name {name} not found")
549
559
 
550
- def checkpoint(self):
560
+ def checkpoint(self, wait: bool = False, timeout_s=300) -> int:
551
561
  """
552
562
  Checkpoints this pipeline, if fault-tolerance is enabled.
553
563
  Fault Tolerance in Feldera: <https://docs.feldera.com/pipelines/fault-tolerance/>
554
564
 
565
+ :param wait: If true, will block until the checkpoint completes.
566
+ :param timeout_s: The maximum time (in seconds) to wait for the checkpoint to complete.
567
+
555
568
  :raises FelderaAPIError: If checkpointing is not enabled.
556
569
  """
557
570
 
558
- self.client.checkpoint_pipeline(self.name)
571
+ seq = self.client.checkpoint_pipeline(self.name)
572
+
573
+ if not wait:
574
+ return seq
575
+
576
+ start = time.time()
577
+
578
+ while True:
579
+ elapsed = time.monotonic() - start
580
+ if elapsed > timeout_s:
581
+ raise TimeoutError(
582
+ f"timeout ({timeout_s}s) reached while waiting for pipeline '{
583
+ self.name
584
+ }' to make checkpoint '{seq}'"
585
+ )
586
+ status = self.checkpoint_status(seq)
587
+ if status == CheckpointStatus.InProgress:
588
+ time.sleep(0.1)
589
+ continue
590
+
591
+ return status
592
+
593
+ return seq
594
+
595
+ def checkpoint_status(self, seq: int) -> CheckpointStatus:
596
+ """
597
+ Checks the status of the given checkpoint.
598
+
599
+ :param seq: The checkpoint sequence number.
600
+ """
601
+
602
+ resp = self.client.checkpoint_pipeline_status(self.name)
603
+ success = resp.get("success")
604
+ if seq == success:
605
+ return CheckpointStatus.Success
606
+
607
+ fail = resp.get("failure") or {}
608
+ if seq == fail.get("sequence_number"):
609
+ failure = CheckpointStatus.Failure
610
+ failure.error = fail.get("error", "")
611
+ return failure
612
+
613
+ if (success is None) or seq > success:
614
+ return CheckpointStatus.InProgress
615
+
616
+ if seq < success:
617
+ return CheckpointStatus.Unknown
618
+
619
+ def sync_checkpoint(self, wait: bool = False, timeout_s=300) -> str:
620
+ """
621
+ Syncs this checkpoint to object store.
622
+
623
+ :param wait: If true, will block until the checkpoint sync opeartion completes.
624
+ :param timeout_s: The maximum time (in seconds) to wait for the checkpoint to complete syncing.
625
+
626
+ :raises FelderaAPIError: If no checkpoints have been made.
627
+ """
628
+
629
+ uuid = self.client.sync_checkpoint(self.name)
630
+
631
+ if not wait:
632
+ return uuid
633
+
634
+ start = time.time()
635
+
636
+ while True:
637
+ elapsed = time.monotonic() - start
638
+ if elapsed > timeout_s:
639
+ raise TimeoutError(
640
+ f"timeout ({timeout_s}s) reached while waiting for pipeline '{
641
+ self.name
642
+ }' to sync checkpoint '{uuid}'"
643
+ )
644
+ status = self.sync_checkpoint_status(uuid)
645
+ if status in [CheckpointStatus.InProgress, CheckpointStatus.Unknown]:
646
+ time.sleep(0.1)
647
+ continue
648
+
649
+ return status
650
+
651
+ return uuid
652
+
653
+ def sync_checkpoint_status(self, uuid: str) -> CheckpointStatus:
654
+ """
655
+ Checks the status of the given checkpoint sync operation.
656
+ If the checkpoint is currently being synchronized, returns
657
+ `CheckpointStatus.Unknown`.
658
+
659
+ :param uuid: The checkpoint uuid.
660
+ """
661
+
662
+ resp = self.client.sync_checkpoint_status(self.name)
663
+ success = resp.get("success")
664
+
665
+ if uuid == success:
666
+ return CheckpointStatus.Success
667
+
668
+ fail = resp.get("failure") or {}
669
+ if uuid == fail.get("uuid"):
670
+ failure = CheckpointStatus.Failure
671
+ failure.error = fail.get("error", "")
672
+ return failure
673
+
674
+ return CheckpointStatus.Unknown
559
675
 
560
676
  def query(self, query: str) -> Generator[Mapping[str, Any], None, None]:
561
677
  """
@@ -7,6 +7,7 @@ from decimal import Decimal
7
7
  from typing import Generator
8
8
 
9
9
  from feldera.rest.config import Config
10
+ from feldera.rest.feldera_config import FelderaConfig
10
11
  from feldera.rest.errors import FelderaTimeoutError
11
12
  from feldera.rest.pipeline import Pipeline
12
13
  from feldera.rest._httprequests import HttpRequests
@@ -381,17 +382,53 @@ Reason: The pipeline is in a FAILED state due to the following error:
381
382
  f"timeout error: pipeline '{pipeline_name}' did not suspend in {timeout_s} seconds"
382
383
  )
383
384
 
384
- def checkpoint_pipeline(self, pipeline_name: str):
385
+ def checkpoint_pipeline(self, pipeline_name: str) -> int:
385
386
  """
386
387
  Checkpoint a fault-tolerant pipeline
387
388
 
388
389
  :param pipeline_name: The name of the pipeline to checkpoint
389
390
  """
390
391
 
391
- self.http.post(
392
+ resp = self.http.post(
392
393
  path=f"/pipelines/{pipeline_name}/checkpoint",
393
394
  )
394
395
 
396
+ return int(resp.get("checkpoint_sequence_number"))
397
+
398
+ def checkpoint_pipeline_status(self, pipeline_name: str) -> dict:
399
+ """
400
+ Gets the checkpoint status
401
+
402
+ :param pipeline_name: The name of the pipeline to check the checkpoint status of.
403
+ """
404
+
405
+ return self.http.get(path=f"/pipelines/{pipeline_name}/checkpoint_status")
406
+
407
+ def sync_checkpoint(self, pipeline_name: str) -> str:
408
+ """
409
+ Triggers a checkpoint synchronization for the specified pipeline.
410
+ Check the status by calling `pipeline_sync_checkpoint_status`.
411
+
412
+ :param pipeline_name: Name of the pipeline whose checkpoint should be synchronized.
413
+ """
414
+
415
+ resp = self.http.post(
416
+ path=f"/pipelines/{pipeline_name}/checkpoint/sync",
417
+ )
418
+
419
+ return resp.get("checkpoint_uuid")
420
+
421
+ def sync_checkpoint_status(self, pipeline_name: str) -> dict:
422
+ """
423
+ Gets the checkpoint sync status of the pipeline
424
+
425
+ :param pipeline_name: The name of the pipeline to check the checkpoint synchronization status of.
426
+ """
427
+
428
+ return self.http.get(
429
+ path=f"/pipelines/{pipeline_name}/checkpoint/sync_status",
430
+ )
431
+
395
432
  def push_to_pipeline(
396
433
  self,
397
434
  pipeline_name: str,
@@ -674,3 +711,12 @@ Reason: The pipeline is in a FAILED state due to the following error:
674
711
  self.http.post(
675
712
  path=f"/pipelines/{pipeline_name}/tables/{table_name}/connectors/{connector_name}/start",
676
713
  )
714
+
715
+ def get_config(self) -> FelderaConfig:
716
+ """
717
+ Get general feldera configuration.
718
+ """
719
+
720
+ resp = self.http.get(path="/config")
721
+
722
+ return FelderaConfig(resp)
@@ -0,0 +1,49 @@
1
+ from enum import Enum
2
+
3
+
4
+ class FelderaEdition(Enum):
5
+ """
6
+ The compilation profile to use when compiling the program.
7
+ Represents the Feldera edition between Enterprise and Open source.
8
+ """
9
+
10
+ ENTERPRISE = "Enterprise"
11
+ """
12
+ The Enterprise version of Feldera.
13
+ """
14
+
15
+ OPEN_SOURCE = "Open source"
16
+ """
17
+ The open source version of Feldera.
18
+ """
19
+
20
+ @staticmethod
21
+ def from_value(value):
22
+ error = None
23
+ if isinstance(value, dict):
24
+ error = value
25
+ value = list(value.keys())[0]
26
+
27
+ for member in FelderaEdition:
28
+ if member.value.lower() == value.lower():
29
+ member.error = error
30
+ return member
31
+ raise ValueError(f"Unknown value '{value}' for enum {FelderaEdition.__name__}")
32
+
33
+ def is_enterprise(self):
34
+ return self == FelderaEdition.ENTERPRISE
35
+
36
+
37
+ class FelderaConfig:
38
+ """
39
+ General configuration of the current Feldera instance.
40
+ """
41
+
42
+ def __init__(self, cfg: dict):
43
+ self.changelog_url = cfg.get("changelog_url")
44
+ self.edition = FelderaEdition.from_value(cfg.get("edition"))
45
+ self.license_validity = cfg.get("license_validity")
46
+ self.revision = cfg.get("revision")
47
+ self.telemetry = cfg.get("telemetry")
48
+ self.update_info = cfg.get("update_info")
49
+ self.version = cfg.get("version")
feldera/runtime_config.py CHANGED
@@ -63,7 +63,7 @@ class RuntimeConfig:
63
63
  def __init__(
64
64
  self,
65
65
  workers: Optional[int] = None,
66
- storage: Optional[Storage] = None,
66
+ storage: Optional[Storage | bool] = None,
67
67
  tracing: Optional[bool] = False,
68
68
  tracing_endpoint_jaeger: Optional[str] = "",
69
69
  cpu_profiler: bool = True,
@@ -74,7 +74,6 @@ class RuntimeConfig:
74
74
  resources: Optional[Resources] = None,
75
75
  ):
76
76
  self.workers = workers
77
- self.storage = storage
78
77
  self.tracing = tracing
79
78
  self.tracing_endpoint_jaeger = tracing_endpoint_jaeger
80
79
  self.cpu_profiler = cpu_profiler
@@ -84,6 +83,10 @@ class RuntimeConfig:
84
83
  self.provisioning_timeout_secs = provisioning_timeout_secs
85
84
  if resources is not None:
86
85
  self.resources = resources.__dict__
86
+ if isinstance(storage, bool):
87
+ self.storage = storage
88
+ if isinstance(storage, Storage):
89
+ self.storage = storage.__dict__
87
90
 
88
91
  @classmethod
89
92
  def from_dict(cls, d: Mapping[str, Any]):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: feldera
3
- Version: 0.68.0
3
+ Version: 0.69.0
4
4
  Summary: The feldera python client
5
5
  Author-email: Abhinav <abhinav.gyawali@feldera.com>
6
6
  License: MIT
@@ -1,20 +1,21 @@
1
1
  feldera/__init__.py,sha256=PxkgCtEAuFwo4u8NGEDio-bF3M-GnbeV45tAQVoBbqE,297
2
2
  feldera/_callback_runner.py,sha256=Tdf6BXN4zppyoy8t_y-Ooa3B0wEfvyezMHU9jxY2ZhA,4713
3
3
  feldera/_helpers.py,sha256=rN0WuGSCCQlXWFMimZUQrgs-LJAfUo074d79sLElncQ,3023
4
- feldera/enums.py,sha256=lgGTn0ahOj-IE2M8k-uszq6Dl98kSX2EeqKDixJcWOo,7373
4
+ feldera/enums.py,sha256=ydzCQHno6as8rXUuoRYw4QE1QXCfpADJwGQZNjMS58A,7774
5
5
  feldera/output_handler.py,sha256=64J3ljhOaKIhxdjOKYi-BUz_HnMwROfmN8eE-btYygU,1930
6
- feldera/pipeline.py,sha256=gDadQHgUHi4bdrMJ9GdIshmFU80pcTNvDiveC5S-GZ4,31100
6
+ feldera/pipeline.py,sha256=SRUT9p6_-00zCljPNNRkA7eLKUqSJszGovEnFhssnk4,34731
7
7
  feldera/pipeline_builder.py,sha256=4rmklRZ0-otvTUb-HTESfNsJopEK-E2jxpJXiYlKpps,3664
8
- feldera/runtime_config.py,sha256=DDJTSzG6LCTH0lVnuUjpATAf1STwAYJOB38xyZh_BJI,3367
8
+ feldera/runtime_config.py,sha256=EDvnakyaRTHUS8GLd6TqLZ_jsGp7_4fbcPnSfQCw1k0,3501
9
9
  feldera/rest/__init__.py,sha256=Eg-EKUU3RSTDcdxTR_7wNDnCly8VpXEzsZCQUmf-y2M,308
10
10
  feldera/rest/_httprequests.py,sha256=e22YbpzOzy7MGo7hk9MOU7ZRTj3F314grY0Ygr-_goI,6636
11
11
  feldera/rest/config.py,sha256=DYzZKngDEhouTEwqVFd-rDrBN9tWqsU07Jl_BTT4mXs,1008
12
12
  feldera/rest/errors.py,sha256=b4i2JjrbSmej7jdko_FL8UeXklLKenSipwMT80jowaM,1720
13
- feldera/rest/feldera_client.py,sha256=ELXZbTfy1UdeIY-ZbFv0kPIdq9qSF5WuoQpdhxmK2pg,23184
13
+ feldera/rest/feldera_client.py,sha256=03XZ8gjgviy3uVvu_IWSoz61yVN8zeH1-ugNHdBMldc,24693
14
+ feldera/rest/feldera_config.py,sha256=1pnGbLFMSLvp7Qh_OlPLALSKCSHIktNWKvx6gYU00U4,1374
14
15
  feldera/rest/pipeline.py,sha256=a1lx-64SYak5mHX5yKElVijdfaAt5sDYVhVIXCJ97QQ,2839
15
16
  feldera/rest/sql_table.py,sha256=qrw-YwMzx5T81zDefNO1KOx7EyypFz1vPwGBzSUB7kc,652
16
17
  feldera/rest/sql_view.py,sha256=hN12mPM0mvwLCIPYywpb12s9Hd2Ws31IlTMXPriMisw,644
17
- feldera-0.68.0.dist-info/METADATA,sha256=eeHpjt5upQ0AGe3L5bEcIwgQmuVQVXo0qaFINpSyyUc,2594
18
- feldera-0.68.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
19
- feldera-0.68.0.dist-info/top_level.txt,sha256=fB6yTqrQiO6RCbY1xP2T_mpPoTjDFtJvkJJodiee7d0,8
20
- feldera-0.68.0.dist-info/RECORD,,
18
+ feldera-0.69.0.dist-info/METADATA,sha256=qxtXNShHN2kByrsGjPiQjPKPnvwRs3hAJZ1ojxRDDAc,2594
19
+ feldera-0.69.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
20
+ feldera-0.69.0.dist-info/top_level.txt,sha256=fB6yTqrQiO6RCbY1xP2T_mpPoTjDFtJvkJJodiee7d0,8
21
+ feldera-0.69.0.dist-info/RECORD,,