feldera 0.111.0__tar.gz → 0.113.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 feldera might be problematic. Click here for more details.
- {feldera-0.111.0 → feldera-0.113.0}/PKG-INFO +5 -5
- {feldera-0.111.0 → feldera-0.113.0}/README.md +4 -4
- {feldera-0.111.0 → feldera-0.113.0}/feldera/_callback_runner.py +3 -1
- {feldera-0.111.0 → feldera-0.113.0}/feldera/pipeline.py +8 -6
- {feldera-0.111.0 → feldera-0.113.0}/feldera/rest/_httprequests.py +50 -33
- {feldera-0.111.0 → feldera-0.113.0}/feldera/rest/feldera_client.py +15 -8
- {feldera-0.111.0 → feldera-0.113.0}/feldera.egg-info/PKG-INFO +5 -5
- {feldera-0.111.0 → feldera-0.113.0}/pyproject.toml +1 -1
- {feldera-0.111.0 → feldera-0.113.0}/tests/test_shared_pipeline0.py +1 -30
- {feldera-0.111.0 → feldera-0.113.0}/tests/test_shared_pipeline1.py +30 -4
- {feldera-0.111.0 → feldera-0.113.0}/feldera/__init__.py +0 -0
- {feldera-0.111.0 → feldera-0.113.0}/feldera/_helpers.py +0 -0
- {feldera-0.111.0 → feldera-0.113.0}/feldera/enums.py +0 -0
- {feldera-0.111.0 → feldera-0.113.0}/feldera/output_handler.py +0 -0
- {feldera-0.111.0 → feldera-0.113.0}/feldera/pipeline_builder.py +0 -0
- {feldera-0.111.0 → feldera-0.113.0}/feldera/rest/__init__.py +0 -0
- {feldera-0.111.0 → feldera-0.113.0}/feldera/rest/_helpers.py +0 -0
- {feldera-0.111.0 → feldera-0.113.0}/feldera/rest/config.py +0 -0
- {feldera-0.111.0 → feldera-0.113.0}/feldera/rest/errors.py +0 -0
- {feldera-0.111.0 → feldera-0.113.0}/feldera/rest/feldera_config.py +0 -0
- {feldera-0.111.0 → feldera-0.113.0}/feldera/rest/pipeline.py +0 -0
- {feldera-0.111.0 → feldera-0.113.0}/feldera/rest/sql_table.py +0 -0
- {feldera-0.111.0 → feldera-0.113.0}/feldera/rest/sql_view.py +0 -0
- {feldera-0.111.0 → feldera-0.113.0}/feldera/runtime_config.py +0 -0
- {feldera-0.111.0 → feldera-0.113.0}/feldera/stats.py +0 -0
- {feldera-0.111.0 → feldera-0.113.0}/feldera.egg-info/SOURCES.txt +0 -0
- {feldera-0.111.0 → feldera-0.113.0}/feldera.egg-info/dependency_links.txt +0 -0
- {feldera-0.111.0 → feldera-0.113.0}/feldera.egg-info/requires.txt +0 -0
- {feldera-0.111.0 → feldera-0.113.0}/feldera.egg-info/top_level.txt +0 -0
- {feldera-0.111.0 → feldera-0.113.0}/setup.cfg +0 -0
- {feldera-0.111.0 → feldera-0.113.0}/tests/test_pipeline_builder.py +0 -0
- {feldera-0.111.0 → feldera-0.113.0}/tests/test_udf.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: feldera
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.113.0
|
|
4
4
|
Summary: The feldera python client
|
|
5
5
|
Author-email: Feldera Team <dev@feldera.com>
|
|
6
6
|
License: MIT
|
|
@@ -92,14 +92,14 @@ To run tests from a specific file:
|
|
|
92
92
|
(cd python && python3 -m pytest ./tests/path-to-file.py)
|
|
93
93
|
```
|
|
94
94
|
|
|
95
|
-
#### Running
|
|
95
|
+
#### Running Tests
|
|
96
96
|
|
|
97
|
-
The
|
|
98
|
-
|
|
97
|
+
The tests validate end-to-end correctness of SQL functionality. To
|
|
98
|
+
run the tests use:
|
|
99
99
|
|
|
100
100
|
```bash
|
|
101
101
|
cd python
|
|
102
|
-
PYTHONPATH=`pwd`
|
|
102
|
+
PYTHONPATH=`pwd` ./tests/run-all-tests.sh
|
|
103
103
|
```
|
|
104
104
|
|
|
105
105
|
### Reducing Compilation Cycles
|
|
@@ -69,14 +69,14 @@ To run tests from a specific file:
|
|
|
69
69
|
(cd python && python3 -m pytest ./tests/path-to-file.py)
|
|
70
70
|
```
|
|
71
71
|
|
|
72
|
-
#### Running
|
|
72
|
+
#### Running Tests
|
|
73
73
|
|
|
74
|
-
The
|
|
75
|
-
|
|
74
|
+
The tests validate end-to-end correctness of SQL functionality. To
|
|
75
|
+
run the tests use:
|
|
76
76
|
|
|
77
77
|
```bash
|
|
78
78
|
cd python
|
|
79
|
-
PYTHONPATH=`pwd`
|
|
79
|
+
PYTHONPATH=`pwd` ./tests/run-all-tests.sh
|
|
80
80
|
```
|
|
81
81
|
|
|
82
82
|
### Reducing Compilation Cycles
|
|
@@ -75,7 +75,9 @@ class CallbackRunner(Thread):
|
|
|
75
75
|
# stop blocking the main thread on `join` for the previous message
|
|
76
76
|
self.queue.task_done()
|
|
77
77
|
|
|
78
|
-
|
|
78
|
+
iterator = gen_obj()
|
|
79
|
+
|
|
80
|
+
for chunk in iterator:
|
|
79
81
|
chunk: dict = chunk
|
|
80
82
|
data: Optional[list[dict]] = chunk.get("json_data")
|
|
81
83
|
seq_no: Optional[int] = chunk.get("sequence_number")
|
|
@@ -484,7 +484,9 @@ metrics"""
|
|
|
484
484
|
for view_name, queue in self.views_tx.pop().items():
|
|
485
485
|
# block until the callback runner has been stopped
|
|
486
486
|
queue.join()
|
|
487
|
+
import time
|
|
487
488
|
|
|
489
|
+
time.sleep(3)
|
|
488
490
|
self.client.stop_pipeline(self.name, force=force, timeout_s=timeout_s)
|
|
489
491
|
|
|
490
492
|
def resume(self, timeout_s: Optional[float] = None):
|
|
@@ -838,14 +840,14 @@ pipeline '{self.name}' to sync checkpoint '{uuid}'"""
|
|
|
838
840
|
|
|
839
841
|
def set_runtime_config(self, runtime_config: RuntimeConfig):
|
|
840
842
|
"""Updates the runtime config of the pipeline. The pipeline
|
|
841
|
-
must be stopped
|
|
842
|
-
|
|
843
|
+
must be stopped. Changing some pipeline configuration, such
|
|
844
|
+
as the number of workers, requires storage to be cleared.
|
|
843
845
|
|
|
844
|
-
For example, to set 'min_batch_size_records' on a pipeline
|
|
846
|
+
For example, to set 'min_batch_size_records' on a pipeline::
|
|
845
847
|
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
848
|
+
runtime_config = pipeline.runtime_config()
|
|
849
|
+
runtime_config.min_batch_size_records = 500
|
|
850
|
+
pipeline.set_runtime_config(runtime_config)
|
|
849
851
|
|
|
850
852
|
"""
|
|
851
853
|
|
|
@@ -12,6 +12,7 @@ import json
|
|
|
12
12
|
import requests
|
|
13
13
|
from requests.packages import urllib3
|
|
14
14
|
from typing import Callable, Optional, Any, Union, Mapping, Sequence, List
|
|
15
|
+
import time
|
|
15
16
|
|
|
16
17
|
|
|
17
18
|
def json_serialize(body: Any) -> str:
|
|
@@ -42,6 +43,7 @@ class HttpRequests:
|
|
|
42
43
|
params: Optional[Mapping[str, Any]] = None,
|
|
43
44
|
stream: bool = False,
|
|
44
45
|
serialize: bool = True,
|
|
46
|
+
max_retries: int = 3,
|
|
45
47
|
) -> Any:
|
|
46
48
|
"""
|
|
47
49
|
:param http_method: The HTTP method to use. Takes the equivalent `requests.*` module. (Example: `requests.get`)
|
|
@@ -68,39 +70,54 @@ class HttpRequests:
|
|
|
68
70
|
str(params),
|
|
69
71
|
)
|
|
70
72
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
73
|
+
for attempt in range(max_retries):
|
|
74
|
+
if http_method.__name__ == "get":
|
|
75
|
+
request = http_method(
|
|
76
|
+
request_path,
|
|
77
|
+
timeout=timeout,
|
|
78
|
+
headers=headers,
|
|
79
|
+
params=params,
|
|
80
|
+
stream=stream,
|
|
81
|
+
verify=self.requests_verify,
|
|
82
|
+
)
|
|
83
|
+
elif isinstance(body, bytes):
|
|
84
|
+
request = http_method(
|
|
85
|
+
request_path,
|
|
86
|
+
timeout=timeout,
|
|
87
|
+
headers=headers,
|
|
88
|
+
data=body,
|
|
89
|
+
params=params,
|
|
90
|
+
stream=stream,
|
|
91
|
+
verify=self.requests_verify,
|
|
92
|
+
)
|
|
93
|
+
else:
|
|
94
|
+
request = http_method(
|
|
95
|
+
request_path,
|
|
96
|
+
timeout=timeout,
|
|
97
|
+
headers=headers,
|
|
98
|
+
data=json_serialize(body) if serialize else body,
|
|
99
|
+
params=params,
|
|
100
|
+
stream=stream,
|
|
101
|
+
verify=self.requests_verify,
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
try:
|
|
105
|
+
resp = self.__validate(request, stream=stream)
|
|
106
|
+
logging.debug("got response: %s", str(resp))
|
|
107
|
+
return resp
|
|
108
|
+
except FelderaAPIError as err:
|
|
109
|
+
# Only retry on 503
|
|
110
|
+
if err.status_code == 503:
|
|
111
|
+
if attempt < max_retries:
|
|
112
|
+
logging.warning(
|
|
113
|
+
"HTTP 503 received for %s, retrying (%d/%d)...",
|
|
114
|
+
path,
|
|
115
|
+
attempt + 1,
|
|
116
|
+
max_retries,
|
|
117
|
+
)
|
|
118
|
+
time.sleep(2) # backoff, adjust as needed
|
|
119
|
+
continue
|
|
120
|
+
raise # re-raise for all other errors or if out of retries
|
|
104
121
|
|
|
105
122
|
except requests.exceptions.Timeout as err:
|
|
106
123
|
raise FelderaTimeoutError(str(err)) from err
|
|
@@ -272,7 +272,11 @@ class FelderaClient:
|
|
|
272
272
|
|
|
273
273
|
if status == "Running":
|
|
274
274
|
break
|
|
275
|
-
elif
|
|
275
|
+
elif (
|
|
276
|
+
status == "Stopped"
|
|
277
|
+
and len(resp.deployment_error or {}) > 0
|
|
278
|
+
and resp.deployment_desired_status == "Stopped"
|
|
279
|
+
):
|
|
276
280
|
raise RuntimeError(
|
|
277
281
|
f"""Unable to START the pipeline.
|
|
278
282
|
Reason: The pipeline is in a STOPPED state due to the following error:
|
|
@@ -601,13 +605,16 @@ Reason: The pipeline is in a STOPPED state due to the following error:
|
|
|
601
605
|
|
|
602
606
|
end = time.monotonic() + timeout if timeout else None
|
|
603
607
|
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
608
|
+
def generator():
|
|
609
|
+
# Using the default chunk size below makes `iter_lines` extremely
|
|
610
|
+
# inefficient when dealing with long lines.
|
|
611
|
+
for chunk in resp.iter_lines(chunk_size=50000000):
|
|
612
|
+
if end and time.monotonic() > end:
|
|
613
|
+
break
|
|
614
|
+
if chunk:
|
|
615
|
+
yield json.loads(chunk, parse_float=Decimal)
|
|
616
|
+
|
|
617
|
+
return generator
|
|
611
618
|
|
|
612
619
|
def query_as_text(
|
|
613
620
|
self, pipeline_name: str, query: str
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: feldera
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.113.0
|
|
4
4
|
Summary: The feldera python client
|
|
5
5
|
Author-email: Feldera Team <dev@feldera.com>
|
|
6
6
|
License: MIT
|
|
@@ -92,14 +92,14 @@ To run tests from a specific file:
|
|
|
92
92
|
(cd python && python3 -m pytest ./tests/path-to-file.py)
|
|
93
93
|
```
|
|
94
94
|
|
|
95
|
-
#### Running
|
|
95
|
+
#### Running Tests
|
|
96
96
|
|
|
97
|
-
The
|
|
98
|
-
|
|
97
|
+
The tests validate end-to-end correctness of SQL functionality. To
|
|
98
|
+
run the tests use:
|
|
99
99
|
|
|
100
100
|
```bash
|
|
101
101
|
cd python
|
|
102
|
-
PYTHONPATH=`pwd`
|
|
102
|
+
PYTHONPATH=`pwd` ./tests/run-all-tests.sh
|
|
103
103
|
```
|
|
104
104
|
|
|
105
105
|
### Reducing Compilation Cycles
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import os
|
|
2
2
|
import pathlib
|
|
3
|
-
import threading
|
|
4
3
|
import pandas as pd
|
|
5
4
|
import time
|
|
6
5
|
import unittest
|
|
@@ -68,35 +67,6 @@ class TestPipeline(SharedTestPipeline):
|
|
|
68
67
|
TEST_CLIENT.pause_pipeline(self.pipeline.name)
|
|
69
68
|
TEST_CLIENT.stop_pipeline(self.pipeline.name, force=True)
|
|
70
69
|
|
|
71
|
-
def __listener(self):
|
|
72
|
-
gen_obj = TEST_CLIENT.listen_to_pipeline(
|
|
73
|
-
pipeline_name=self.pipeline.name,
|
|
74
|
-
table_name="v0",
|
|
75
|
-
format="csv",
|
|
76
|
-
)
|
|
77
|
-
counter = 0
|
|
78
|
-
for chunk in gen_obj:
|
|
79
|
-
counter += 1
|
|
80
|
-
text_data = chunk.get("text_data")
|
|
81
|
-
if text_data:
|
|
82
|
-
assert text_data == "1,1\n2,1\n"
|
|
83
|
-
self.result = True
|
|
84
|
-
break
|
|
85
|
-
if counter > 10:
|
|
86
|
-
self.result = False
|
|
87
|
-
break
|
|
88
|
-
|
|
89
|
-
def test_listen_to_pipeline(self):
|
|
90
|
-
data = "1\n2\n"
|
|
91
|
-
TEST_CLIENT.pause_pipeline(self.pipeline.name)
|
|
92
|
-
t1 = threading.Thread(target=self.__listener)
|
|
93
|
-
t1.start()
|
|
94
|
-
self.pipeline.resume()
|
|
95
|
-
TEST_CLIENT.push_to_pipeline(self.pipeline.name, "tbl", "csv", data)
|
|
96
|
-
t1.join()
|
|
97
|
-
assert self.result
|
|
98
|
-
TEST_CLIENT.stop_pipeline(self.pipeline.name, force=True)
|
|
99
|
-
|
|
100
70
|
def test_adhoc_query_text(self):
|
|
101
71
|
data = "1\n2\n"
|
|
102
72
|
self.pipeline.start()
|
|
@@ -567,6 +537,7 @@ class TestPipeline(SharedTestPipeline):
|
|
|
567
537
|
with self.assertRaises(ValueError):
|
|
568
538
|
data = {"m_var": {None: 1}}
|
|
569
539
|
self.pipeline.input_json("tbl_map_issue3754", [data])
|
|
540
|
+
self.pipeline.stop(force=True)
|
|
570
541
|
|
|
571
542
|
def test_pipeline_resource_config(self):
|
|
572
543
|
from feldera.runtime_config import Resources, RuntimeConfig
|
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
import random
|
|
2
|
+
from uuid import uuid4
|
|
2
3
|
import time
|
|
4
|
+
import os
|
|
3
5
|
from typing import Optional
|
|
4
6
|
from feldera.runtime_config import RuntimeConfig, Storage
|
|
5
7
|
from tests import enterprise_only
|
|
6
8
|
from tests.shared_test_pipeline import SharedTestPipeline
|
|
7
9
|
|
|
8
10
|
|
|
9
|
-
DEFAULT_ENDPOINT =
|
|
11
|
+
DEFAULT_ENDPOINT = os.environ.get(
|
|
12
|
+
"DEFAULT_MINIO_ENDPOINT", "http://minio.extra.svc.cluster.local:9000"
|
|
13
|
+
)
|
|
10
14
|
DEFAULT_BUCKET = "default"
|
|
11
15
|
ACCESS_KEY = "minio"
|
|
12
16
|
SECRET_KEY = "miniopasswd"
|
|
@@ -36,7 +40,13 @@ def storage_cfg(
|
|
|
36
40
|
|
|
37
41
|
class TestCheckpointSync(SharedTestPipeline):
|
|
38
42
|
@enterprise_only
|
|
39
|
-
def test_checkpoint_sync(
|
|
43
|
+
def test_checkpoint_sync(
|
|
44
|
+
self,
|
|
45
|
+
from_uuid: bool = False,
|
|
46
|
+
random_uuid: bool = False,
|
|
47
|
+
clear_storage: bool = True,
|
|
48
|
+
auth_err: bool = False,
|
|
49
|
+
):
|
|
40
50
|
"""
|
|
41
51
|
CREATE TABLE t0 (c0 INT, c1 VARCHAR);
|
|
42
52
|
CREATE MATERIALIZED VIEW v0 AS SELECT c0 FROM t0;
|
|
@@ -56,7 +66,12 @@ class TestCheckpointSync(SharedTestPipeline):
|
|
|
56
66
|
uuid = self.pipeline.sync_checkpoint(wait=True)
|
|
57
67
|
|
|
58
68
|
self.pipeline.stop(force=True)
|
|
59
|
-
|
|
69
|
+
|
|
70
|
+
if clear_storage:
|
|
71
|
+
self.pipeline.clear_storage()
|
|
72
|
+
|
|
73
|
+
if random_uuid:
|
|
74
|
+
uuid = uuid4()
|
|
60
75
|
|
|
61
76
|
# Restart pipeline from checkpoint
|
|
62
77
|
storage_config = storage_cfg(
|
|
@@ -69,13 +84,24 @@ class TestCheckpointSync(SharedTestPipeline):
|
|
|
69
84
|
self.assertCountEqual(got_before, got_after)
|
|
70
85
|
|
|
71
86
|
self.pipeline.stop(force=True)
|
|
72
|
-
|
|
87
|
+
|
|
88
|
+
if clear_storage:
|
|
89
|
+
self.pipeline.clear_storage()
|
|
73
90
|
|
|
74
91
|
@enterprise_only
|
|
75
92
|
def test_checkpoint_sync_from_uuid(self):
|
|
76
93
|
self.test_checkpoint_sync(from_uuid=True)
|
|
77
94
|
|
|
95
|
+
@enterprise_only
|
|
96
|
+
def test_checkpoint_sync_without_clearing_storage(self):
|
|
97
|
+
self.test_checkpoint_sync(clear_storage=False)
|
|
98
|
+
|
|
78
99
|
@enterprise_only
|
|
79
100
|
def test_checkpoint_sync_err(self):
|
|
80
101
|
with self.assertRaisesRegex(RuntimeError, "SignatureDoesNotMatch"):
|
|
81
102
|
self.test_checkpoint_sync(auth_err=True)
|
|
103
|
+
|
|
104
|
+
@enterprise_only
|
|
105
|
+
def test_checkpoint_sync_err_nonexistent_checkpoint(self):
|
|
106
|
+
with self.assertRaisesRegex(RuntimeError, "were not found in source"):
|
|
107
|
+
self.test_checkpoint_sync(random_uuid=True, from_uuid=True)
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|