buelon 1.0.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.
- buelon-1.0.0/MANIFEST.in +5 -0
- buelon-1.0.0/PKG-INFO +309 -0
- buelon-1.0.0/README.md +284 -0
- buelon-1.0.0/buelon/__init__.py +10 -0
- buelon-1.0.0/buelon/bucket.py +386 -0
- buelon-1.0.0/buelon/command_line.py +328 -0
- buelon-1.0.0/buelon/core/__init__.py +19 -0
- buelon-1.0.0/buelon/core/action.py +67 -0
- buelon-1.0.0/buelon/core/execution.py +314 -0
- buelon-1.0.0/buelon/core/loop.py +135 -0
- buelon-1.0.0/buelon/core/pipe.py +117 -0
- buelon-1.0.0/buelon/core/pipe_debug.py +89 -0
- buelon-1.0.0/buelon/core/pipe_interpreter.py +189 -0
- buelon-1.0.0/buelon/core/step.py +189 -0
- buelon-1.0.0/buelon/core/step_definition.py +142 -0
- buelon-1.0.0/buelon/cython/__init__.py +10 -0
- buelon-1.0.0/buelon/cython/c_bucket.c +19624 -0
- buelon-1.0.0/buelon/cython/c_bucket.pyx +380 -0
- buelon-1.0.0/buelon/cython/c_hub.c +30669 -0
- buelon-1.0.0/buelon/cython/c_hub.pyx +699 -0
- buelon-1.0.0/buelon/cython/c_worker.c +18582 -0
- buelon-1.0.0/buelon/cython/c_worker.pyx +260 -0
- buelon-1.0.0/buelon/examples/__init__.py +8 -0
- buelon-1.0.0/buelon/examples/demo.py +77 -0
- buelon-1.0.0/buelon/examples/example.pipe +86 -0
- buelon-1.0.0/buelon/examples/example.py +198 -0
- buelon-1.0.0/buelon/helpers/__init__.py +12 -0
- buelon-1.0.0/buelon/helpers/json_parser.py +84 -0
- buelon-1.0.0/buelon/helpers/pipe_util.py +218 -0
- buelon-1.0.0/buelon/helpers/postgres.py +426 -0
- buelon-1.0.0/buelon/helpers/sqlite3_helper.py +194 -0
- buelon-1.0.0/buelon/hub.py +705 -0
- buelon-1.0.0/buelon/worker.py +265 -0
- buelon-1.0.0/buelon.egg-info/PKG-INFO +309 -0
- buelon-1.0.0/buelon.egg-info/SOURCES.txt +40 -0
- buelon-1.0.0/buelon.egg-info/dependency_links.txt +1 -0
- buelon-1.0.0/buelon.egg-info/entry_points.txt +2 -0
- buelon-1.0.0/buelon.egg-info/requires.txt +6 -0
- buelon-1.0.0/buelon.egg-info/top_level.txt +1 -0
- buelon-1.0.0/pyproject.toml +2 -0
- buelon-1.0.0/setup.cfg +4 -0
- buelon-1.0.0/setup.py +61 -0
buelon-1.0.0/MANIFEST.in
ADDED
buelon-1.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: buelon
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: A system and language to handle any process using multiple workers for some(planned for most) languages
|
|
5
|
+
Home-page: https://github.com/daniel-olson-code/buelon
|
|
6
|
+
Author: Daniel Olson
|
|
7
|
+
Author-email: daniel@orphos.cloud
|
|
8
|
+
Keywords: buelon etl pipeline asynchronous data-processing api
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Requires-Python: >=3.10
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
Requires-Dist: Cython
|
|
20
|
+
Requires-Dist: psycopg2-binary
|
|
21
|
+
Requires-Dist: orjson
|
|
22
|
+
Requires-Dist: python-dotenv
|
|
23
|
+
Requires-Dist: asyncio-pool
|
|
24
|
+
Requires-Dist: psutil
|
|
25
|
+
|
|
26
|
+
# Pipeline
|
|
27
|
+
|
|
28
|
+
Pipeline is an asynchronous ETL (Extract, Transform, Load) system that uses a custom scripting language to run code across multiple servers, one step at a time. It's designed for efficient handling of large-scale data processing tasks, particularly those involving APIs with long wait times or I/O-heavy workloads.
|
|
29
|
+
|
|
30
|
+
## Table of Contents
|
|
31
|
+
- [Features](#features)
|
|
32
|
+
- [Installation](#installation)
|
|
33
|
+
- [Quick Start](#quick-start)
|
|
34
|
+
- [Supported Languages](#supported-languages)
|
|
35
|
+
- [Configuration](#configuration)
|
|
36
|
+
- [Usage](#usage)
|
|
37
|
+
- [Learn by Example](#learn-by-example)
|
|
38
|
+
- [Performance](#performance) <!--- - [Contributing](#contributing) -->
|
|
39
|
+
- [Future of Pipeline](#plans)
|
|
40
|
+
- [License](#license)
|
|
41
|
+
|
|
42
|
+
## Features
|
|
43
|
+
- Asynchronous execution of code across multiple servers
|
|
44
|
+
- Custom scripting language for defining ETL pipelines
|
|
45
|
+
- Support for Python, SQLite3, and PostgreSQL
|
|
46
|
+
- Efficient handling of APIs with long wait times
|
|
47
|
+
- Optimized for I/O-heavy workloads
|
|
48
|
+
- Scalable architecture for processing large amounts of data
|
|
49
|
+
|
|
50
|
+
## Installation
|
|
51
|
+
1. Clone the repository: `git clone https://github.com/yourusername/pipeline.git
|
|
52
|
+
cd pipeline`
|
|
53
|
+
2. Install required packages: `pip install -r requirements.txt`
|
|
54
|
+
3. (Optional) Build Cython files: `python build.py` (This can give a 3x performance boost)
|
|
55
|
+
4. (Optional) Configure PostgreSQL settings in the `.env` file.
|
|
56
|
+
|
|
57
|
+
## Quick Start
|
|
58
|
+
1. Run the demo server: `python demo.py`
|
|
59
|
+
2. In a separate terminal, run the example uploading code: `python example.py`
|
|
60
|
+
|
|
61
|
+
## Supported Languages
|
|
62
|
+
- Python
|
|
63
|
+
- SQLite3
|
|
64
|
+
- PostgreSQL
|
|
65
|
+
|
|
66
|
+
## Configuration
|
|
67
|
+
* Setup at least 4 servers on a private network (they can be small, you can technically run all these on one server like `demo.py` does but that's not recommended)
|
|
68
|
+
* Create a server running `python bucket.py` or something like `python -c "import c_bucket;c_bucket.main()"`
|
|
69
|
+
* Create a server running `python pipeline.py` or something like `python -c "import c_pipeline;c_pipeline.main()"`
|
|
70
|
+
* Create a server running `python worker.py` or something like `python -c "import c_worker;c_worker.main()"`
|
|
71
|
+
* Edit the `.env` on each server to access the private ip. Change `PIPE_WORKER_HOST` to refer to the server running `pipeline.py` on server running `worker.py` and change `BUCKET_CLIENT_HOST` to refer to the server running `bucket.py` on both the `worker.py` server and the `pipeline.py` server
|
|
72
|
+
* Add "worker" servers until desired speed
|
|
73
|
+
* Create a server with private and public network access and use this to run `pipeline.upload_pipe_code_from_file` or `pipeline.upload_pipe_code` uploading the script to the server to be run.
|
|
74
|
+
* All workers must also have the files necessary to run your code, pip installs and all
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
* (Optionally) The `PIPE_WORKER_SUBPROCESS_JOBS` value within the `.env` file can be set to `true` or `false`(really anything but true). This configuration lets you run python code in a subprocess or within the "worker" script. Setting it to false gives a very slight performance increase, but requires you restart the server every time you make a change to your project.
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
## Usage
|
|
81
|
+
|
|
82
|
+
Pipeline uses a custom scripting language to define ETL processes. Here's how to use it:
|
|
83
|
+
|
|
84
|
+
### Basic Structure
|
|
85
|
+
|
|
86
|
+
A Pipeline script consists of steps and pipes. Each step defines a task, and pipes determine the order of execution.
|
|
87
|
+
|
|
88
|
+
```python
|
|
89
|
+
# Step definition
|
|
90
|
+
step_name:
|
|
91
|
+
language
|
|
92
|
+
function_or_table_name
|
|
93
|
+
source_file_or_code
|
|
94
|
+
|
|
95
|
+
# Pipe definition
|
|
96
|
+
pipe_name = step1 | step2 | step3
|
|
97
|
+
|
|
98
|
+
# Execution
|
|
99
|
+
pipe_name()
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Supported Languages
|
|
103
|
+
|
|
104
|
+
- python: For Python code
|
|
105
|
+
- sqlite3: For SQLite queries
|
|
106
|
+
- postgres: For PostgreSQL queries
|
|
107
|
+
|
|
108
|
+
### Scopes and Priorities
|
|
109
|
+
|
|
110
|
+
Use scopes and priorities to control execution:
|
|
111
|
+
|
|
112
|
+
```python
|
|
113
|
+
$ production # Set default scope
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
step_name:
|
|
117
|
+
python
|
|
118
|
+
!9 # Set priority (higher numbers run first within their scope)
|
|
119
|
+
$ testing # Set a lower priority scope
|
|
120
|
+
function_name
|
|
121
|
+
source_file
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Writing Code Directly
|
|
125
|
+
|
|
126
|
+
For short snippets, you can write code directly in the script:
|
|
127
|
+
|
|
128
|
+
```python
|
|
129
|
+
step_name:
|
|
130
|
+
sqlite3
|
|
131
|
+
table_name
|
|
132
|
+
`
|
|
133
|
+
SELECT * FROM table_name
|
|
134
|
+
WHERE condition = 'value'
|
|
135
|
+
`
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Defining Pipes
|
|
139
|
+
|
|
140
|
+
Pipes determine the order of step execution:
|
|
141
|
+
|
|
142
|
+
```python
|
|
143
|
+
single_pipe = | step1 # or `step1 |`
|
|
144
|
+
normal_pipe = step1 | step2 | step3
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### Executing Pipes
|
|
148
|
+
|
|
149
|
+
There are two ways to execute pipes:
|
|
150
|
+
|
|
151
|
+
#### Single call
|
|
152
|
+
|
|
153
|
+
```python
|
|
154
|
+
pipe1()
|
|
155
|
+
result1 = pipe2()
|
|
156
|
+
result2 = pipe3(result1)
|
|
157
|
+
pipe4(result2)
|
|
158
|
+
|
|
159
|
+
pipe5(result1, result2)
|
|
160
|
+
|
|
161
|
+
# incorrect --> `pipe3(pipe2())` # this syntax is currently not supported
|
|
162
|
+
# also incorrect, they must be on one line as of now:
|
|
163
|
+
# `pipe3(
|
|
164
|
+
# result1
|
|
165
|
+
# )`
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
#### Looped execution
|
|
169
|
+
|
|
170
|
+
```python
|
|
171
|
+
for item in pipe1():
|
|
172
|
+
pipe2(item)
|
|
173
|
+
# incorrect --> `for item in pipe1(result):` # syntax not supported for now
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### Running Your Pipeline
|
|
177
|
+
|
|
178
|
+
- Save your pipeline script as a .pipe file.
|
|
179
|
+
- Use the Pipeline API to upload and run your script:
|
|
180
|
+
```python
|
|
181
|
+
# example.py
|
|
182
|
+
import pipeline
|
|
183
|
+
|
|
184
|
+
pipeline.upload_pipe_code_from_file('your_script.pipe')
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
## Learn by Example
|
|
189
|
+
|
|
190
|
+
```python
|
|
191
|
+
# the default scope is set to `production-small` for all steps (imports)
|
|
192
|
+
# setting scopes is how you make new steps with errors
|
|
193
|
+
# not slow down your servers by setting them to a lower scope.
|
|
194
|
+
# And/or how you handle processes that either require and do not require big machines to run
|
|
195
|
+
$ production-small
|
|
196
|
+
|
|
197
|
+
# step 1: `accounts`
|
|
198
|
+
accounts:
|
|
199
|
+
python # <-- select the language to be run. currently only python, sqlite3 and postgres are available
|
|
200
|
+
accounts # define the function or table name that will be used
|
|
201
|
+
example.py # either provide a file or write code directly using the "`" char (see below example)
|
|
202
|
+
|
|
203
|
+
request:
|
|
204
|
+
python
|
|
205
|
+
request_report
|
|
206
|
+
example.py
|
|
207
|
+
|
|
208
|
+
status:
|
|
209
|
+
python
|
|
210
|
+
$ testing-small # <-- "scope" for a single step. A lower scope will be given less priority over higher scopes. See PIPE_WORKER_SCOPES in `.env` file
|
|
211
|
+
get_status
|
|
212
|
+
example.py
|
|
213
|
+
|
|
214
|
+
download:
|
|
215
|
+
python
|
|
216
|
+
!9 # <-- "priority" higher numbers are more important and run first within their scope.
|
|
217
|
+
get_report
|
|
218
|
+
example.py
|
|
219
|
+
|
|
220
|
+
manipulate_data:
|
|
221
|
+
sqlite3
|
|
222
|
+
some_table # *vvvv* see below for writing code directly *vvvv*
|
|
223
|
+
`
|
|
224
|
+
SELECT
|
|
225
|
+
*,
|
|
226
|
+
CASE
|
|
227
|
+
WHEN sales = 0
|
|
228
|
+
THEN 0.0
|
|
229
|
+
ELSE spend / sales
|
|
230
|
+
END AS acos
|
|
231
|
+
FROM some_table
|
|
232
|
+
`
|
|
233
|
+
|
|
234
|
+
## this one's just to show postgres as well
|
|
235
|
+
#manipulate_data_again:
|
|
236
|
+
# postgres
|
|
237
|
+
# another_table
|
|
238
|
+
# `
|
|
239
|
+
#select
|
|
240
|
+
# *,
|
|
241
|
+
# case
|
|
242
|
+
# when spend = 0
|
|
243
|
+
# then 0.0
|
|
244
|
+
# else sales / spend
|
|
245
|
+
# end AS roas
|
|
246
|
+
#from another_table
|
|
247
|
+
#`
|
|
248
|
+
|
|
249
|
+
upload:
|
|
250
|
+
python
|
|
251
|
+
upload_to_db
|
|
252
|
+
example.py
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
# these are pipes and what will tell the server what order to run the steps
|
|
256
|
+
# and also transfer the returned data between steps
|
|
257
|
+
# each step will be run individually and could be run on a different computer each time
|
|
258
|
+
accounts_pipe = | accounts # single pipes currently need a `|` before or behind the value
|
|
259
|
+
api_pipe = request | status | download | manipulate_data | upload
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
# currently there are only two syntax's for "running" pipes.
|
|
263
|
+
# either by itself:
|
|
264
|
+
# pipe()
|
|
265
|
+
#
|
|
266
|
+
# or in a loop:
|
|
267
|
+
# for value in pipe1():
|
|
268
|
+
# pipe2(value)
|
|
269
|
+
|
|
270
|
+
# # Another Example:
|
|
271
|
+
# v = pipe(accounts_pipe) # <-- single call
|
|
272
|
+
# pipe2(v)
|
|
273
|
+
|
|
274
|
+
# right not you cannot pass arguments within the pipe being used for the for loop.
|
|
275
|
+
# in this case `accounts_pipe()` cannot be `accounts_pipe(some_value)`
|
|
276
|
+
for account in accounts_pipe():
|
|
277
|
+
api_pipe(account)
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
## Performance
|
|
281
|
+
Pipeline is specifically designed to handle I/O-heavy workloads efficiently. It excels in scenarios such as:
|
|
282
|
+
|
|
283
|
+
- Making numerous API calls, especially to services with long processing times
|
|
284
|
+
- Handling large-scale data transfers between different systems
|
|
285
|
+
- Concurrent database operations
|
|
286
|
+
|
|
287
|
+
For instance, Pipeline is currently being used by an agency to request 30,000 reports daily from the Amazon Ads API, resulting in at least 90,000 API calls per day. This process, which includes pushing data into a PostgreSQL server with over 600 GB of data, is completed within a few hours(adding more workers could make this alot faster). The system's efficiency allows for this level of performance at a cost of under $100, including database expenses, actually the servers requesting the data are about $25.
|
|
288
|
+
|
|
289
|
+
The asynchronous nature of Pipeline makes it particularly suited for APIs like Amazon Ads, where there are significant wait times between requesting a report and its availability for download. Traditional synchronous ETL processes struggle with such APIs, especially for agencies with numerous profiles.
|
|
290
|
+
|
|
291
|
+
## Plans
|
|
292
|
+
|
|
293
|
+
If this projects sees some love, or I just find more free time, I'd like to support more languages. Even compiled languages such as `rust`, `go` and `c++`. Allowing teams that write different languages to work on the same program.
|
|
294
|
+
|
|
295
|
+
Turning this project into a pip package.
|
|
296
|
+
|
|
297
|
+
I want to rewrite this in rust for performance.
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
<!---
|
|
301
|
+
your comment goes here
|
|
302
|
+
and here
|
|
303
|
+
|
|
304
|
+
## Contributing
|
|
305
|
+
[Contributing guidelines]
|
|
306
|
+
-->
|
|
307
|
+
|
|
308
|
+
## License
|
|
309
|
+
* MIT License
|
buelon-1.0.0/README.md
ADDED
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
# Pipeline
|
|
2
|
+
|
|
3
|
+
Pipeline is an asynchronous ETL (Extract, Transform, Load) system that uses a custom scripting language to run code across multiple servers, one step at a time. It's designed for efficient handling of large-scale data processing tasks, particularly those involving APIs with long wait times or I/O-heavy workloads.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
- [Features](#features)
|
|
7
|
+
- [Installation](#installation)
|
|
8
|
+
- [Quick Start](#quick-start)
|
|
9
|
+
- [Supported Languages](#supported-languages)
|
|
10
|
+
- [Configuration](#configuration)
|
|
11
|
+
- [Usage](#usage)
|
|
12
|
+
- [Learn by Example](#learn-by-example)
|
|
13
|
+
- [Performance](#performance) <!--- - [Contributing](#contributing) -->
|
|
14
|
+
- [Future of Pipeline](#plans)
|
|
15
|
+
- [License](#license)
|
|
16
|
+
|
|
17
|
+
## Features
|
|
18
|
+
- Asynchronous execution of code across multiple servers
|
|
19
|
+
- Custom scripting language for defining ETL pipelines
|
|
20
|
+
- Support for Python, SQLite3, and PostgreSQL
|
|
21
|
+
- Efficient handling of APIs with long wait times
|
|
22
|
+
- Optimized for I/O-heavy workloads
|
|
23
|
+
- Scalable architecture for processing large amounts of data
|
|
24
|
+
|
|
25
|
+
## Installation
|
|
26
|
+
1. Clone the repository: `git clone https://github.com/yourusername/pipeline.git
|
|
27
|
+
cd pipeline`
|
|
28
|
+
2. Install required packages: `pip install -r requirements.txt`
|
|
29
|
+
3. (Optional) Build Cython files: `python build.py` (This can give a 3x performance boost)
|
|
30
|
+
4. (Optional) Configure PostgreSQL settings in the `.env` file.
|
|
31
|
+
|
|
32
|
+
## Quick Start
|
|
33
|
+
1. Run the demo server: `python demo.py`
|
|
34
|
+
2. In a separate terminal, run the example uploading code: `python example.py`
|
|
35
|
+
|
|
36
|
+
## Supported Languages
|
|
37
|
+
- Python
|
|
38
|
+
- SQLite3
|
|
39
|
+
- PostgreSQL
|
|
40
|
+
|
|
41
|
+
## Configuration
|
|
42
|
+
* Setup at least 4 servers on a private network (they can be small, you can technically run all these on one server like `demo.py` does but that's not recommended)
|
|
43
|
+
* Create a server running `python bucket.py` or something like `python -c "import c_bucket;c_bucket.main()"`
|
|
44
|
+
* Create a server running `python pipeline.py` or something like `python -c "import c_pipeline;c_pipeline.main()"`
|
|
45
|
+
* Create a server running `python worker.py` or something like `python -c "import c_worker;c_worker.main()"`
|
|
46
|
+
* Edit the `.env` on each server to access the private ip. Change `PIPE_WORKER_HOST` to refer to the server running `pipeline.py` on server running `worker.py` and change `BUCKET_CLIENT_HOST` to refer to the server running `bucket.py` on both the `worker.py` server and the `pipeline.py` server
|
|
47
|
+
* Add "worker" servers until desired speed
|
|
48
|
+
* Create a server with private and public network access and use this to run `pipeline.upload_pipe_code_from_file` or `pipeline.upload_pipe_code` uploading the script to the server to be run.
|
|
49
|
+
* All workers must also have the files necessary to run your code, pip installs and all
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
* (Optionally) The `PIPE_WORKER_SUBPROCESS_JOBS` value within the `.env` file can be set to `true` or `false`(really anything but true). This configuration lets you run python code in a subprocess or within the "worker" script. Setting it to false gives a very slight performance increase, but requires you restart the server every time you make a change to your project.
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
## Usage
|
|
56
|
+
|
|
57
|
+
Pipeline uses a custom scripting language to define ETL processes. Here's how to use it:
|
|
58
|
+
|
|
59
|
+
### Basic Structure
|
|
60
|
+
|
|
61
|
+
A Pipeline script consists of steps and pipes. Each step defines a task, and pipes determine the order of execution.
|
|
62
|
+
|
|
63
|
+
```python
|
|
64
|
+
# Step definition
|
|
65
|
+
step_name:
|
|
66
|
+
language
|
|
67
|
+
function_or_table_name
|
|
68
|
+
source_file_or_code
|
|
69
|
+
|
|
70
|
+
# Pipe definition
|
|
71
|
+
pipe_name = step1 | step2 | step3
|
|
72
|
+
|
|
73
|
+
# Execution
|
|
74
|
+
pipe_name()
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Supported Languages
|
|
78
|
+
|
|
79
|
+
- python: For Python code
|
|
80
|
+
- sqlite3: For SQLite queries
|
|
81
|
+
- postgres: For PostgreSQL queries
|
|
82
|
+
|
|
83
|
+
### Scopes and Priorities
|
|
84
|
+
|
|
85
|
+
Use scopes and priorities to control execution:
|
|
86
|
+
|
|
87
|
+
```python
|
|
88
|
+
$ production # Set default scope
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
step_name:
|
|
92
|
+
python
|
|
93
|
+
!9 # Set priority (higher numbers run first within their scope)
|
|
94
|
+
$ testing # Set a lower priority scope
|
|
95
|
+
function_name
|
|
96
|
+
source_file
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Writing Code Directly
|
|
100
|
+
|
|
101
|
+
For short snippets, you can write code directly in the script:
|
|
102
|
+
|
|
103
|
+
```python
|
|
104
|
+
step_name:
|
|
105
|
+
sqlite3
|
|
106
|
+
table_name
|
|
107
|
+
`
|
|
108
|
+
SELECT * FROM table_name
|
|
109
|
+
WHERE condition = 'value'
|
|
110
|
+
`
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### Defining Pipes
|
|
114
|
+
|
|
115
|
+
Pipes determine the order of step execution:
|
|
116
|
+
|
|
117
|
+
```python
|
|
118
|
+
single_pipe = | step1 # or `step1 |`
|
|
119
|
+
normal_pipe = step1 | step2 | step3
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Executing Pipes
|
|
123
|
+
|
|
124
|
+
There are two ways to execute pipes:
|
|
125
|
+
|
|
126
|
+
#### Single call
|
|
127
|
+
|
|
128
|
+
```python
|
|
129
|
+
pipe1()
|
|
130
|
+
result1 = pipe2()
|
|
131
|
+
result2 = pipe3(result1)
|
|
132
|
+
pipe4(result2)
|
|
133
|
+
|
|
134
|
+
pipe5(result1, result2)
|
|
135
|
+
|
|
136
|
+
# incorrect --> `pipe3(pipe2())` # this syntax is currently not supported
|
|
137
|
+
# also incorrect, they must be on one line as of now:
|
|
138
|
+
# `pipe3(
|
|
139
|
+
# result1
|
|
140
|
+
# )`
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
#### Looped execution
|
|
144
|
+
|
|
145
|
+
```python
|
|
146
|
+
for item in pipe1():
|
|
147
|
+
pipe2(item)
|
|
148
|
+
# incorrect --> `for item in pipe1(result):` # syntax not supported for now
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### Running Your Pipeline
|
|
152
|
+
|
|
153
|
+
- Save your pipeline script as a .pipe file.
|
|
154
|
+
- Use the Pipeline API to upload and run your script:
|
|
155
|
+
```python
|
|
156
|
+
# example.py
|
|
157
|
+
import pipeline
|
|
158
|
+
|
|
159
|
+
pipeline.upload_pipe_code_from_file('your_script.pipe')
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
## Learn by Example
|
|
164
|
+
|
|
165
|
+
```python
|
|
166
|
+
# the default scope is set to `production-small` for all steps (imports)
|
|
167
|
+
# setting scopes is how you make new steps with errors
|
|
168
|
+
# not slow down your servers by setting them to a lower scope.
|
|
169
|
+
# And/or how you handle processes that either require and do not require big machines to run
|
|
170
|
+
$ production-small
|
|
171
|
+
|
|
172
|
+
# step 1: `accounts`
|
|
173
|
+
accounts:
|
|
174
|
+
python # <-- select the language to be run. currently only python, sqlite3 and postgres are available
|
|
175
|
+
accounts # define the function or table name that will be used
|
|
176
|
+
example.py # either provide a file or write code directly using the "`" char (see below example)
|
|
177
|
+
|
|
178
|
+
request:
|
|
179
|
+
python
|
|
180
|
+
request_report
|
|
181
|
+
example.py
|
|
182
|
+
|
|
183
|
+
status:
|
|
184
|
+
python
|
|
185
|
+
$ testing-small # <-- "scope" for a single step. A lower scope will be given less priority over higher scopes. See PIPE_WORKER_SCOPES in `.env` file
|
|
186
|
+
get_status
|
|
187
|
+
example.py
|
|
188
|
+
|
|
189
|
+
download:
|
|
190
|
+
python
|
|
191
|
+
!9 # <-- "priority" higher numbers are more important and run first within their scope.
|
|
192
|
+
get_report
|
|
193
|
+
example.py
|
|
194
|
+
|
|
195
|
+
manipulate_data:
|
|
196
|
+
sqlite3
|
|
197
|
+
some_table # *vvvv* see below for writing code directly *vvvv*
|
|
198
|
+
`
|
|
199
|
+
SELECT
|
|
200
|
+
*,
|
|
201
|
+
CASE
|
|
202
|
+
WHEN sales = 0
|
|
203
|
+
THEN 0.0
|
|
204
|
+
ELSE spend / sales
|
|
205
|
+
END AS acos
|
|
206
|
+
FROM some_table
|
|
207
|
+
`
|
|
208
|
+
|
|
209
|
+
## this one's just to show postgres as well
|
|
210
|
+
#manipulate_data_again:
|
|
211
|
+
# postgres
|
|
212
|
+
# another_table
|
|
213
|
+
# `
|
|
214
|
+
#select
|
|
215
|
+
# *,
|
|
216
|
+
# case
|
|
217
|
+
# when spend = 0
|
|
218
|
+
# then 0.0
|
|
219
|
+
# else sales / spend
|
|
220
|
+
# end AS roas
|
|
221
|
+
#from another_table
|
|
222
|
+
#`
|
|
223
|
+
|
|
224
|
+
upload:
|
|
225
|
+
python
|
|
226
|
+
upload_to_db
|
|
227
|
+
example.py
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
# these are pipes and what will tell the server what order to run the steps
|
|
231
|
+
# and also transfer the returned data between steps
|
|
232
|
+
# each step will be run individually and could be run on a different computer each time
|
|
233
|
+
accounts_pipe = | accounts # single pipes currently need a `|` before or behind the value
|
|
234
|
+
api_pipe = request | status | download | manipulate_data | upload
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
# currently there are only two syntax's for "running" pipes.
|
|
238
|
+
# either by itself:
|
|
239
|
+
# pipe()
|
|
240
|
+
#
|
|
241
|
+
# or in a loop:
|
|
242
|
+
# for value in pipe1():
|
|
243
|
+
# pipe2(value)
|
|
244
|
+
|
|
245
|
+
# # Another Example:
|
|
246
|
+
# v = pipe(accounts_pipe) # <-- single call
|
|
247
|
+
# pipe2(v)
|
|
248
|
+
|
|
249
|
+
# right not you cannot pass arguments within the pipe being used for the for loop.
|
|
250
|
+
# in this case `accounts_pipe()` cannot be `accounts_pipe(some_value)`
|
|
251
|
+
for account in accounts_pipe():
|
|
252
|
+
api_pipe(account)
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
## Performance
|
|
256
|
+
Pipeline is specifically designed to handle I/O-heavy workloads efficiently. It excels in scenarios such as:
|
|
257
|
+
|
|
258
|
+
- Making numerous API calls, especially to services with long processing times
|
|
259
|
+
- Handling large-scale data transfers between different systems
|
|
260
|
+
- Concurrent database operations
|
|
261
|
+
|
|
262
|
+
For instance, Pipeline is currently being used by an agency to request 30,000 reports daily from the Amazon Ads API, resulting in at least 90,000 API calls per day. This process, which includes pushing data into a PostgreSQL server with over 600 GB of data, is completed within a few hours(adding more workers could make this alot faster). The system's efficiency allows for this level of performance at a cost of under $100, including database expenses, actually the servers requesting the data are about $25.
|
|
263
|
+
|
|
264
|
+
The asynchronous nature of Pipeline makes it particularly suited for APIs like Amazon Ads, where there are significant wait times between requesting a report and its availability for download. Traditional synchronous ETL processes struggle with such APIs, especially for agencies with numerous profiles.
|
|
265
|
+
|
|
266
|
+
## Plans
|
|
267
|
+
|
|
268
|
+
If this projects sees some love, or I just find more free time, I'd like to support more languages. Even compiled languages such as `rust`, `go` and `c++`. Allowing teams that write different languages to work on the same program.
|
|
269
|
+
|
|
270
|
+
Turning this project into a pip package.
|
|
271
|
+
|
|
272
|
+
I want to rewrite this in rust for performance.
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
<!---
|
|
276
|
+
your comment goes here
|
|
277
|
+
and here
|
|
278
|
+
|
|
279
|
+
## Contributing
|
|
280
|
+
[Contributing guidelines]
|
|
281
|
+
-->
|
|
282
|
+
|
|
283
|
+
## License
|
|
284
|
+
* MIT License
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# Import modules from subpackages
|
|
2
|
+
from . import bucket, hub, worker, command_line
|
|
3
|
+
from .core import action, execution, loop, pipe, pipe_interpreter, step, step_definition
|
|
4
|
+
from .cython import c_bucket, c_hub, c_worker
|
|
5
|
+
from .helpers import json_parser, pipe_util, postgres, sqlite3_helper
|
|
6
|
+
from .examples import demo, example
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
# Define the public API
|
|
10
|
+
__all__ = ['bucket', 'hub', 'worker', 'command_line', 'core', 'cython', 'helpers', 'cython', 'examples']
|