rulebricks-spark 0.1.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.
Files changed (29) hide show
  1. rulebricks_spark-0.1.0/.gitignore +21 -0
  2. rulebricks_spark-0.1.0/CHANGELOG.md +16 -0
  3. rulebricks_spark-0.1.0/LICENSE +21 -0
  4. rulebricks_spark-0.1.0/PKG-INFO +273 -0
  5. rulebricks_spark-0.1.0/README.md +235 -0
  6. rulebricks_spark-0.1.0/assets/cover.png +0 -0
  7. rulebricks_spark-0.1.0/examples/README.md +42 -0
  8. rulebricks_spark-0.1.0/examples/breach-triage/README.md +145 -0
  9. rulebricks_spark-0.1.0/examples/breach-triage/demo.py +280 -0
  10. rulebricks_spark-0.1.0/examples/sql-callable/README.md +151 -0
  11. rulebricks_spark-0.1.0/examples/sql-callable/demo.py +211 -0
  12. rulebricks_spark-0.1.0/examples/streaming/README.md +96 -0
  13. rulebricks_spark-0.1.0/examples/streaming/demo.py +170 -0
  14. rulebricks_spark-0.1.0/pyproject.toml +100 -0
  15. rulebricks_spark-0.1.0/src/rulebricks_spark/__init__.py +37 -0
  16. rulebricks_spark-0.1.0/src/rulebricks_spark/_config.py +33 -0
  17. rulebricks_spark-0.1.0/src/rulebricks_spark/_partition.py +418 -0
  18. rulebricks_spark-0.1.0/src/rulebricks_spark/_schema.py +249 -0
  19. rulebricks_spark-0.1.0/src/rulebricks_spark/_version.py +3 -0
  20. rulebricks_spark-0.1.0/src/rulebricks_spark/databricks.py +211 -0
  21. rulebricks_spark-0.1.0/src/rulebricks_spark/exceptions.py +26 -0
  22. rulebricks_spark-0.1.0/src/rulebricks_spark/solve.py +241 -0
  23. rulebricks_spark-0.1.0/tests/__init__.py +0 -0
  24. rulebricks_spark-0.1.0/tests/conftest.py +16 -0
  25. rulebricks_spark-0.1.0/tests/test_coerce.py +232 -0
  26. rulebricks_spark-0.1.0/tests/test_integration.py +266 -0
  27. rulebricks_spark-0.1.0/tests/test_register_udf_args.py +51 -0
  28. rulebricks_spark-0.1.0/tests/test_schema.py +162 -0
  29. rulebricks_spark-0.1.0/tests/test_solve_args.py +47 -0
@@ -0,0 +1,21 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *$py.class
4
+ *.egg-info/
5
+ *.egg
6
+ dist/
7
+ build/
8
+ .eggs/
9
+ *.whl
10
+ .mypy_cache/
11
+ .pytest_cache/
12
+ .ruff_cache/
13
+ .tox/
14
+ .venv/
15
+ venv/
16
+ *.so
17
+ .DS_Store
18
+ *.swp
19
+ *.swo
20
+
21
+ demo/
@@ -0,0 +1,16 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0 (2026-04-16)
4
+
5
+ Initial release.
6
+
7
+ - `solve()` — apply a Rulebricks rule to every row of a Spark DataFrame.
8
+ - Automatic batching via `bulk_solve` (configurable, max 1000 rows/call).
9
+ - Intra-partition concurrency with configurable thread pool.
10
+ - Schema inference from rule definition, or explicit `output_schema`.
11
+ - Column collision handling: `on_conflict="overwrite"` (default) or `"error"`.
12
+ - Per-row error isolation in `_rb_error` column with `on_error="continue"`.
13
+ - `input_mapping` for renaming DataFrame columns to match rule fields.
14
+ - Exponential-backoff retries on 408/429/5xx/timeout/connection errors.
15
+ - `auto_repartition` for pathologically partitioned DataFrames.
16
+ - Works in Databricks, EMR, Synapse, Dataproc, and local PySpark.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Prefix Software Inc. (Rulebricks)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,273 @@
1
+ Metadata-Version: 2.4
2
+ Name: rulebricks-spark
3
+ Version: 0.1.0
4
+ Summary: Apache Spark integration for Rulebricks — apply decision-table based rules to Spark DataFrames at scale.
5
+ Project-URL: Homepage, https://rulebricks.com
6
+ Project-URL: Documentation, https://rulebricks.com/docs
7
+ Project-URL: Repository, https://github.com/rulebricks/spark
8
+ Project-URL: Issues, https://github.com/rulebricks/spark/issues
9
+ Project-URL: Changelog, https://github.com/rulebricks/spark/blob/main/CHANGELOG.md
10
+ Author-email: Rulebricks <support@rulebricks.com>
11
+ License: MIT
12
+ License-File: LICENSE
13
+ Keywords: business-rules,databricks,decision-tables,pyspark,rulebricks,rules-engine,spark
14
+ Classifier: Development Status :: 4 - Beta
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Operating System :: OS Independent
18
+ Classifier: Programming Language :: Python :: 3
19
+ Classifier: Programming Language :: Python :: 3.9
20
+ Classifier: Programming Language :: Python :: 3.10
21
+ Classifier: Programming Language :: Python :: 3.11
22
+ Classifier: Programming Language :: Python :: 3.12
23
+ Classifier: Topic :: Scientific/Engineering
24
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
25
+ Requires-Python: >=3.9
26
+ Requires-Dist: pandas>=1.5
27
+ Requires-Dist: pyarrow>=10.0
28
+ Requires-Dist: pyspark>=3.3
29
+ Requires-Dist: rulebricks>=1.0.0
30
+ Provides-Extra: dev
31
+ Requires-Dist: build>=1.0; extra == 'dev'
32
+ Requires-Dist: mypy>=1.0; extra == 'dev'
33
+ Requires-Dist: pytest-cov>=4.0; extra == 'dev'
34
+ Requires-Dist: pytest>=7.0; extra == 'dev'
35
+ Requires-Dist: ruff>=0.4; extra == 'dev'
36
+ Requires-Dist: twine>=4.0; extra == 'dev'
37
+ Description-Content-Type: text/markdown
38
+
39
+ # Rulebricks Spark
40
+
41
+ [![PyPI](https://img.shields.io/pypi/v/rulebricks-spark.svg)](https://pypi.org/project/rulebricks-spark/)
42
+ [![Python](https://img.shields.io/pypi/pyversions/rulebricks-spark.svg)](https://pypi.org/project/rulebricks-spark/)
43
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
44
+
45
+ Apache Spark integration for [Rulebricks](https://rulebricks.com). Apply decision-table based rules to Spark DataFrames at scale.
46
+
47
+ ```python
48
+ from rulebricks_spark import solve
49
+
50
+ scored = solve(
51
+ spark.table("breach_reports"),
52
+ rule="breach-triage",
53
+ api_key=dbutils.secrets.get("rulebricks", "api_key"),
54
+ )
55
+
56
+ scored.display()
57
+ ```
58
+
59
+ ![Apply a Rulebricks rule to a Spark DataFrame — input columns in, rule output columns appended](https://raw.githubusercontent.com/rulebricks/spark/main/assets/cover.png)
60
+
61
+ Rule outputs overwrite existing columns, or automatically create new ones. Works in any PySpark environment — Databricks, EMR, Synapse, or a local cluster.
62
+
63
+ ## Why
64
+
65
+ Changing business & policy logic shouldn't live in notebooks or SQL CASE statements. Rulebricks enables business users to directly author & manage decision tables in a visual UI, with versioning, audit logs, and test suites. `rulebricks-spark` then lets your Spark pipelines consume those rules at scale without wrapping the HTTP API yourself.
66
+
67
+ **Edit a rule in the Rulebricks UI. Rerun your notebook. Different results, no code change.**
68
+
69
+ ## Install
70
+
71
+ ```bash
72
+ pip install rulebricks-spark
73
+ ```
74
+
75
+ ### Databricks
76
+
77
+ In a notebook cell:
78
+
79
+ ```python
80
+ %pip install rulebricks-spark
81
+ dbutils.library.restartPython()
82
+ ```
83
+
84
+ Or install as a cluster library (PyPI source, package `rulebricks-spark`) so every notebook on the cluster has it available.
85
+
86
+ ## Quickstart
87
+
88
+ ```python
89
+ from rulebricks_spark import solve
90
+
91
+ df = spark.table("claims_raw")
92
+
93
+ scored = solve(
94
+ df,
95
+ rule="claims-triage",
96
+ api_key=dbutils.secrets.get("rulebricks", "api_key"),
97
+ )
98
+
99
+ scored.write.mode("overwrite").saveAsTable("claims_scored")
100
+ ```
101
+
102
+ Rule output fields are appended as new columns. A trailing `_rb_error` column is `null` on success and carries the error message on failure, so you can isolate problem rows without failing the whole job.
103
+
104
+ ## How it works
105
+
106
+ 1. **Driver side:** fetch the rule's response schema once via the Rulebricks SDK, build the Spark output `StructType`.
107
+ 2. **Executor side:** each partition's pandas chunks are split into batches (default 500 rows) and sent to `bulk_solve`. Multiple batches per partition run concurrently in a thread pool (default 4).
108
+ 3. **Result assembly:** rule outputs are coerced to the declared Spark types, collided input columns are overwritten, errors land in `_rb_error`.
109
+
110
+ Effective parallelism = `num_executors × max_concurrent_requests_per_partition`.
111
+
112
+ ## API
113
+
114
+ ### `solve(df, rule, *, api_key, ...) -> DataFrame`
115
+
116
+ | Argument | Default | Description |
117
+ | --------------------------------------- | ------------------------ | ------------------------------------------------------------------ |
118
+ | `df` | — | Input Spark DataFrame. |
119
+ | `rule` | — | Rulebricks rule slug. |
120
+ | `api_key` | — | Your Rulebricks API key. |
121
+ | `base_url` | `https://rulebricks.com` | Override for self-hosted deployments. |
122
+ | `batch_size` | `500` | Rows per `bulk_solve` call (max 1000). |
123
+ | `max_concurrent_requests_per_partition` | `4` | Concurrent batches per Spark partition. |
124
+ | `input_mapping` | `None` | `{df_column: rule_field}` for renaming inputs. |
125
+ | `output_schema` | `None` | Explicit `StructType` to skip schema inference. |
126
+ | `on_conflict` | `"overwrite"` | `"error"` or `"overwrite"` when output names collide with inputs. |
127
+ | `on_missing_input` | `"default"` | `"error"` or `"default"` (use rule's declared defaults). |
128
+ | `on_error` | `"continue"` | `"raise"` to fail the job; `"continue"` to isolate in `_rb_error`. |
129
+ | `auto_repartition` | `False` | Repartition a single-partition DataFrame for parallelism. |
130
+ | `timeout_seconds` | `60.0` | Per-request timeout. |
131
+ | `max_retries` | `3` | Retry attempts for 408/429/5xx/timeout/reset. |
132
+
133
+ Returns a new DataFrame. Spark DataFrames are immutable — `solve()` does not mutate the input.
134
+
135
+ ## Common patterns
136
+
137
+ ### Column names don't match the rule's inputs
138
+
139
+ ```python
140
+ solve(
141
+ df,
142
+ rule="eligibility",
143
+ api_key=key,
144
+ input_mapping={
145
+ "fin_impact": "financial_impact",
146
+ "dso": "days_since_occurrence",
147
+ },
148
+ )
149
+ ```
150
+
151
+ Unmapped columns pass through as-is.
152
+
153
+ ### Call from SQL via Unity Catalog
154
+
155
+ Register the scored DataFrame as a view, or wrap `solve()` in a Python function that your analysts can use:
156
+
157
+ ```python
158
+ def triage_breaches(input_table: str, output_table: str):
159
+ df = spark.table(input_table)
160
+ scored = solve(df, rule="breach-triage", api_key=get_key())
161
+ scored.write.mode("overwrite").saveAsTable(output_table)
162
+ ```
163
+
164
+ ### Self-hosted Rulebricks
165
+
166
+ ```python
167
+ solve(
168
+ df,
169
+ rule="underwriting",
170
+ api_key=key,
171
+ base_url="https://rules.acme.internal",
172
+ )
173
+ ```
174
+
175
+ ### High-throughput tuning
176
+
177
+ For large batch jobs against a self-hosted Rulebricks instance:
178
+
179
+ ```python
180
+ solve(
181
+ df.repartition(32), # match executor count
182
+ rule="score",
183
+ api_key=key,
184
+ batch_size=1000, # max
185
+ max_concurrent_requests_per_partition=8,
186
+ )
187
+ ```
188
+
189
+ Effective throughput: 32 partitions × 8 concurrent × 1000 rows = 256k rows in flight.
190
+
191
+ ### Inspecting errors
192
+
193
+ ```python
194
+ scored = solve(df, rule="score", api_key=key, on_error="continue")
195
+
196
+ # Successful rows
197
+ scored.filter("_rb_error IS NULL")
198
+
199
+ # Failed rows, for investigation
200
+ scored.filter("_rb_error IS NOT NULL").select("report_id", "_rb_error").show(truncate=False)
201
+ ```
202
+
203
+ ### Explicit output schema (typed arrays / structs)
204
+
205
+ By default, rule outputs of type `list` and `object` are serialized as JSON strings. To get typed `ArrayType` or `StructType` columns, pass an explicit `output_schema`:
206
+
207
+ ```python
208
+ from pyspark.sql.types import StructType, StructField, StringType, ArrayType, DoubleType
209
+
210
+ schema = StructType([
211
+ StructField("priority", StringType()),
212
+ StructField("tags", ArrayType(StringType())),
213
+ StructField("score", DoubleType()),
214
+ ])
215
+
216
+ solve(df, rule="triage", api_key=key, output_schema=schema)
217
+ ```
218
+
219
+ ## Performance notes
220
+
221
+ - **Batch size**: 500 is a reasonable default. Raise to 1000 for simple rules, and drop to 100–200 for rules involving large payloads.
222
+ - **Concurrency**: each executor hits the Rulebricks API in parallel. Against the managed cloud, rate and account limits may kick in before you saturate workers — watch for 429s in `_rb_error`. Against a self-hosted cluster, the autoscaler handles it. [Read more about self-hosted Rulebricks for production volumes](#self-hosted-rulebricks-for-production-volumes).
223
+ - **Partition count**: `solve()` inherits the input DataFrame's partitioning. If your DataFrame has one huge partition, either call `.repartition(n)` yourself or pass `auto_repartition=True`.
224
+ - **Schema inference cost**: a single driver-side API call at job start. Cache the schema or pass `output_schema` explicitly to skip it for repeated runs.
225
+
226
+ ## Error handling
227
+
228
+ The `_rb_error` column is always present in the output.
229
+
230
+ - `on_error="continue"` (default): batch failures mark every row in the failed batch with the error string, leaving rule output columns `null`. The job completes.
231
+ - `on_error="raise"`: the Spark task fails on the first batch error, propagating to job failure.
232
+
233
+ Retries run automatically for 408 / 425 / 429 / 500 / 502 / 503 / 504, timeouts, and connection resets, using exponential backoff (2^attempt seconds, capped at 30).
234
+
235
+ ## Requirements
236
+
237
+ - Python 3.9+
238
+ - PySpark 3.3+
239
+ - `rulebricks` SDK 1.0+
240
+ - `pandas` 1.5+
241
+ - `pyarrow` 10+
242
+
243
+ ## Development
244
+
245
+ ```bash
246
+ git clone https://github.com/rulebricks/spark
247
+ cd rulebricks-spark
248
+ pip install -e ".[dev]"
249
+ pytest
250
+ ```
251
+
252
+ Run only the fast unit tests:
253
+
254
+ ```bash
255
+ pytest -m "not requires_spark"
256
+ ```
257
+
258
+ Run the end-to-end tests (requires PySpark and a JVM):
259
+
260
+ ```bash
261
+ pytest -m requires_spark
262
+ ```
263
+
264
+ ## License
265
+
266
+ MIT — see [LICENSE](LICENSE).
267
+
268
+ ## Links
269
+
270
+ - [Rulebricks Homepage](https://rulebricks.com)
271
+ - [Rulebricks User Guide](https://rulebricks.com/docs)
272
+ - [Python SDK reference](https://rulebricks.com/docs/api-reference#sdk/python)
273
+ - [Issues & feature requests](https://github.com/rulebricks/spark/issues)
@@ -0,0 +1,235 @@
1
+ # Rulebricks Spark
2
+
3
+ [![PyPI](https://img.shields.io/pypi/v/rulebricks-spark.svg)](https://pypi.org/project/rulebricks-spark/)
4
+ [![Python](https://img.shields.io/pypi/pyversions/rulebricks-spark.svg)](https://pypi.org/project/rulebricks-spark/)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
6
+
7
+ Apache Spark integration for [Rulebricks](https://rulebricks.com). Apply decision-table based rules to Spark DataFrames at scale.
8
+
9
+ ```python
10
+ from rulebricks_spark import solve
11
+
12
+ scored = solve(
13
+ spark.table("breach_reports"),
14
+ rule="breach-triage",
15
+ api_key=dbutils.secrets.get("rulebricks", "api_key"),
16
+ )
17
+
18
+ scored.display()
19
+ ```
20
+
21
+ ![Apply a Rulebricks rule to a Spark DataFrame — input columns in, rule output columns appended](https://raw.githubusercontent.com/rulebricks/spark/main/assets/cover.png)
22
+
23
+ Rule outputs overwrite existing columns, or automatically create new ones. Works in any PySpark environment — Databricks, EMR, Synapse, or a local cluster.
24
+
25
+ ## Why
26
+
27
+ Changing business & policy logic shouldn't live in notebooks or SQL CASE statements. Rulebricks enables business users to directly author & manage decision tables in a visual UI, with versioning, audit logs, and test suites. `rulebricks-spark` then lets your Spark pipelines consume those rules at scale without wrapping the HTTP API yourself.
28
+
29
+ **Edit a rule in the Rulebricks UI. Rerun your notebook. Different results, no code change.**
30
+
31
+ ## Install
32
+
33
+ ```bash
34
+ pip install rulebricks-spark
35
+ ```
36
+
37
+ ### Databricks
38
+
39
+ In a notebook cell:
40
+
41
+ ```python
42
+ %pip install rulebricks-spark
43
+ dbutils.library.restartPython()
44
+ ```
45
+
46
+ Or install as a cluster library (PyPI source, package `rulebricks-spark`) so every notebook on the cluster has it available.
47
+
48
+ ## Quickstart
49
+
50
+ ```python
51
+ from rulebricks_spark import solve
52
+
53
+ df = spark.table("claims_raw")
54
+
55
+ scored = solve(
56
+ df,
57
+ rule="claims-triage",
58
+ api_key=dbutils.secrets.get("rulebricks", "api_key"),
59
+ )
60
+
61
+ scored.write.mode("overwrite").saveAsTable("claims_scored")
62
+ ```
63
+
64
+ Rule output fields are appended as new columns. A trailing `_rb_error` column is `null` on success and carries the error message on failure, so you can isolate problem rows without failing the whole job.
65
+
66
+ ## How it works
67
+
68
+ 1. **Driver side:** fetch the rule's response schema once via the Rulebricks SDK, build the Spark output `StructType`.
69
+ 2. **Executor side:** each partition's pandas chunks are split into batches (default 500 rows) and sent to `bulk_solve`. Multiple batches per partition run concurrently in a thread pool (default 4).
70
+ 3. **Result assembly:** rule outputs are coerced to the declared Spark types, collided input columns are overwritten, errors land in `_rb_error`.
71
+
72
+ Effective parallelism = `num_executors × max_concurrent_requests_per_partition`.
73
+
74
+ ## API
75
+
76
+ ### `solve(df, rule, *, api_key, ...) -> DataFrame`
77
+
78
+ | Argument | Default | Description |
79
+ | --------------------------------------- | ------------------------ | ------------------------------------------------------------------ |
80
+ | `df` | — | Input Spark DataFrame. |
81
+ | `rule` | — | Rulebricks rule slug. |
82
+ | `api_key` | — | Your Rulebricks API key. |
83
+ | `base_url` | `https://rulebricks.com` | Override for self-hosted deployments. |
84
+ | `batch_size` | `500` | Rows per `bulk_solve` call (max 1000). |
85
+ | `max_concurrent_requests_per_partition` | `4` | Concurrent batches per Spark partition. |
86
+ | `input_mapping` | `None` | `{df_column: rule_field}` for renaming inputs. |
87
+ | `output_schema` | `None` | Explicit `StructType` to skip schema inference. |
88
+ | `on_conflict` | `"overwrite"` | `"error"` or `"overwrite"` when output names collide with inputs. |
89
+ | `on_missing_input` | `"default"` | `"error"` or `"default"` (use rule's declared defaults). |
90
+ | `on_error` | `"continue"` | `"raise"` to fail the job; `"continue"` to isolate in `_rb_error`. |
91
+ | `auto_repartition` | `False` | Repartition a single-partition DataFrame for parallelism. |
92
+ | `timeout_seconds` | `60.0` | Per-request timeout. |
93
+ | `max_retries` | `3` | Retry attempts for 408/429/5xx/timeout/reset. |
94
+
95
+ Returns a new DataFrame. Spark DataFrames are immutable — `solve()` does not mutate the input.
96
+
97
+ ## Common patterns
98
+
99
+ ### Column names don't match the rule's inputs
100
+
101
+ ```python
102
+ solve(
103
+ df,
104
+ rule="eligibility",
105
+ api_key=key,
106
+ input_mapping={
107
+ "fin_impact": "financial_impact",
108
+ "dso": "days_since_occurrence",
109
+ },
110
+ )
111
+ ```
112
+
113
+ Unmapped columns pass through as-is.
114
+
115
+ ### Call from SQL via Unity Catalog
116
+
117
+ Register the scored DataFrame as a view, or wrap `solve()` in a Python function that your analysts can use:
118
+
119
+ ```python
120
+ def triage_breaches(input_table: str, output_table: str):
121
+ df = spark.table(input_table)
122
+ scored = solve(df, rule="breach-triage", api_key=get_key())
123
+ scored.write.mode("overwrite").saveAsTable(output_table)
124
+ ```
125
+
126
+ ### Self-hosted Rulebricks
127
+
128
+ ```python
129
+ solve(
130
+ df,
131
+ rule="underwriting",
132
+ api_key=key,
133
+ base_url="https://rules.acme.internal",
134
+ )
135
+ ```
136
+
137
+ ### High-throughput tuning
138
+
139
+ For large batch jobs against a self-hosted Rulebricks instance:
140
+
141
+ ```python
142
+ solve(
143
+ df.repartition(32), # match executor count
144
+ rule="score",
145
+ api_key=key,
146
+ batch_size=1000, # max
147
+ max_concurrent_requests_per_partition=8,
148
+ )
149
+ ```
150
+
151
+ Effective throughput: 32 partitions × 8 concurrent × 1000 rows = 256k rows in flight.
152
+
153
+ ### Inspecting errors
154
+
155
+ ```python
156
+ scored = solve(df, rule="score", api_key=key, on_error="continue")
157
+
158
+ # Successful rows
159
+ scored.filter("_rb_error IS NULL")
160
+
161
+ # Failed rows, for investigation
162
+ scored.filter("_rb_error IS NOT NULL").select("report_id", "_rb_error").show(truncate=False)
163
+ ```
164
+
165
+ ### Explicit output schema (typed arrays / structs)
166
+
167
+ By default, rule outputs of type `list` and `object` are serialized as JSON strings. To get typed `ArrayType` or `StructType` columns, pass an explicit `output_schema`:
168
+
169
+ ```python
170
+ from pyspark.sql.types import StructType, StructField, StringType, ArrayType, DoubleType
171
+
172
+ schema = StructType([
173
+ StructField("priority", StringType()),
174
+ StructField("tags", ArrayType(StringType())),
175
+ StructField("score", DoubleType()),
176
+ ])
177
+
178
+ solve(df, rule="triage", api_key=key, output_schema=schema)
179
+ ```
180
+
181
+ ## Performance notes
182
+
183
+ - **Batch size**: 500 is a reasonable default. Raise to 1000 for simple rules, and drop to 100–200 for rules involving large payloads.
184
+ - **Concurrency**: each executor hits the Rulebricks API in parallel. Against the managed cloud, rate and account limits may kick in before you saturate workers — watch for 429s in `_rb_error`. Against a self-hosted cluster, the autoscaler handles it. [Read more about self-hosted Rulebricks for production volumes](#self-hosted-rulebricks-for-production-volumes).
185
+ - **Partition count**: `solve()` inherits the input DataFrame's partitioning. If your DataFrame has one huge partition, either call `.repartition(n)` yourself or pass `auto_repartition=True`.
186
+ - **Schema inference cost**: a single driver-side API call at job start. Cache the schema or pass `output_schema` explicitly to skip it for repeated runs.
187
+
188
+ ## Error handling
189
+
190
+ The `_rb_error` column is always present in the output.
191
+
192
+ - `on_error="continue"` (default): batch failures mark every row in the failed batch with the error string, leaving rule output columns `null`. The job completes.
193
+ - `on_error="raise"`: the Spark task fails on the first batch error, propagating to job failure.
194
+
195
+ Retries run automatically for 408 / 425 / 429 / 500 / 502 / 503 / 504, timeouts, and connection resets, using exponential backoff (2^attempt seconds, capped at 30).
196
+
197
+ ## Requirements
198
+
199
+ - Python 3.9+
200
+ - PySpark 3.3+
201
+ - `rulebricks` SDK 1.0+
202
+ - `pandas` 1.5+
203
+ - `pyarrow` 10+
204
+
205
+ ## Development
206
+
207
+ ```bash
208
+ git clone https://github.com/rulebricks/spark
209
+ cd rulebricks-spark
210
+ pip install -e ".[dev]"
211
+ pytest
212
+ ```
213
+
214
+ Run only the fast unit tests:
215
+
216
+ ```bash
217
+ pytest -m "not requires_spark"
218
+ ```
219
+
220
+ Run the end-to-end tests (requires PySpark and a JVM):
221
+
222
+ ```bash
223
+ pytest -m requires_spark
224
+ ```
225
+
226
+ ## License
227
+
228
+ MIT — see [LICENSE](LICENSE).
229
+
230
+ ## Links
231
+
232
+ - [Rulebricks Homepage](https://rulebricks.com)
233
+ - [Rulebricks User Guide](https://rulebricks.com/docs)
234
+ - [Python SDK reference](https://rulebricks.com/docs/api-reference#sdk/python)
235
+ - [Issues & feature requests](https://github.com/rulebricks/spark/issues)
Binary file
@@ -0,0 +1,42 @@
1
+ # Examples
2
+
3
+ Runnable Databricks notebooks demonstrating `rulebricks-spark` across the
4
+ three most common patterns.
5
+
6
+ | Example | Pattern |
7
+ |---|---|
8
+ | [`breach-triage/`](./breach-triage/) | Canonical batch: score a Delta table end-to-end. Start here. |
9
+ | [`streaming/`](./streaming/) | Apply a rule to a Structured Streaming DataFrame with a Delta sink. |
10
+ | [`sql-callable/`](./sql-callable/) | Register a SQL-callable UDF for use in `%sql` cells and DBSQL. |
11
+
12
+ Each folder has its own README with the rule schema the notebook expects,
13
+ where to find your rule slug and API key, and how to wire the notebook into
14
+ Databricks (secrets, cluster config, `%pip install`).
15
+
16
+ ## Prerequisites (all examples)
17
+
18
+ - A published rule in your Rulebricks workspace.
19
+ - A Rulebricks API key.
20
+ - A PySpark environment — Databricks, EMR, Synapse, or a local PySpark
21
+ install. Examples are formatted as Databricks notebooks but the core
22
+ `solve()` and `register_udf()` calls work anywhere PySpark runs.
23
+
24
+ ## Running outside Databricks
25
+
26
+ The `%pip`, `dbutils`, `display`, and `%sql` cells are Databricks-specific
27
+ conveniences. For local or non-Databricks runs:
28
+
29
+ ```python
30
+ import os
31
+ api_key = os.environ["RULEBRICKS_API_KEY"]
32
+ ```
33
+
34
+ Replace `display(df)` with `df.show()` and skip the `%sql` cells (or wrap
35
+ them in `spark.sql("...").show()`).
36
+
37
+ ## A note on rule slugs
38
+
39
+ Rulebricks slugs are short opaque IDs — roughly 10 characters of nanoid-
40
+ style text, e.g. `abc123xyz9`. They appear in the rule editor URL and on
41
+ each rule card in the Dashboard. Copy yours into the `RULE_SLUG` constant
42
+ at the top of each notebook.