Rill is a lightweight, single-node micro-batching engine designed for high-performance streaming analytics using pure PyArrow and zero-copy DuckDB.
By bypassing traditional Python data structures and the Global Interpreter Lock (GIL) where possible, Rill ingests continuous event streams directly into C++ contiguous memory (pyarrow.Table). It leverages a scheduled processing trigger to buffer, join, aggregate, and query incoming data efficiently without the overhead of event-by-event Python loops.
From raw data ingestion to complex multi-stream joins, aggregations, scheduled SQL pipelines, and structured alerting, every operation is strictly contained within C++ vectorized memory, making Rill the ideal zero-infrastructure solution for live data processing.
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β RILL ENGINE β
β β
β βββββββββββββββββββββββββββ Micro-Batch Loop βββββββββββββββββββββββββ β
β β Input Connectors β (Trigger Interval) β Table Registry β β
β β β β β β
β β β’ Memory / JSON Stream β ββββββββββββββββββββββββ> β β’ Snapshot (PK Upsert)β β
β β β’ WebSocket / Kafka β β β’ Append-Only (TTL) β β
β β (Backpressure Buffer) β β (z_insert/z_update) β β
β βββββββββββββββββββββββββββ βββββββββββββ¬ββββββββββββ β
β β β β
β βββββββββββββββββββββββββββ β β
β βΌ βΌ β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Compute Transformations β β
β β β β
β β β’ Zero-Copy DuckDB Scheduled SQL Pipelines (Live Tables In-Memory) β β
β β β’ Multi-Stream Relational Joins & Aggregations β β
β β β’ Vectorized TTL Pruning (Age / Row Count) β β
β βββββββββββββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββββββββββββββ β
β β β
β βΌ β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Persistent Metrics & Telemetry β β
β β β β
β β β’ Dual-Connection Topology: Writes business metrics & engine telemetry β β
β β to an on-disk `rill_metrics.db` for isolated serving and dashboarding. β β
β β β’ Structured Alerts Pipeline: Engine exceptions & user-pushed alerts β β
β β written to `rill_alerts` table for dashboard visibility. β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
- Snapshot Mode (
mode="snapshot"or"upsert"): Maintains unique state tables by atomically replacing matching rows whenprimary_keyis defined (pc.is_in/left antijoin). TTL (RetentionPolicy) is optional. - Append-Only Mode (
mode="append"): Every incoming micro-batch is appended strictly without overwriting previous rows, even if a primary key or ID column is present. - Mandatory TTL Governance: Because append-only event streams grow continuously with every event, Rill strictly enforces that a
RetentionPolicy(max_rowsormax_age_seconds) is provided for append-only tables (mode="append"), preventing unbounded streams from exhausting system RAM over time.
Every table inside Rill automatically maintains two system timestamps (pa.float64() unix seconds since epoch):
z_insert_ts: Recorded when a record first arrives. During primary-key upserts, existing records preserve their originalz_insert_tsvia zero-copy C++ lookup.z_update_ts: Automatically refreshed to the current timestamp on every modification.- Governed TTL: By default,
RetentionPolicy(max_age_seconds=...)usesz_insert_ts(time_column="z_insert_ts") to accurately evict aged rows during each micro-batch tick.
Use rills.schema([fields], primary_key="user_id", mode="append") to embed governance properties directly inside PyArrow schema metadata (schema.metadata[b"primary_key"]). When a RillTable is initialized with an enriched schema, it automatically extracts its primary key and operating mode without manual boilerplate.
Configure RillEngine(memory_budget_bytes=...) or memory_budget_mb=... along with an optional on_memory_warning callback. During every micro-batch iteration (step()), Rill monitors pa.total_allocated_bytes() across all C++ memory pools and emits a ResourceWarning if the memory threshold is crossed.
Continuously join two live stream tables (left_table, right_table) via C++ relational hash-joins (pc.is_in / Table.join) and calculate real-time aggregations (sum, count, avg, min, max) without exiting PyArrow memory.
Input connectors (MemoryConnector, JSONStreamConnector) enforce configurable limits (max_buffer_records, max_buffer_bytes) paired with overflow strategies ("drop_oldest", "drop_newest", "error") that slice excess data cleanly across batch boundaries.
Execute standard SQL queries across any live pyarrow.Table using zero-copy DuckDB (duckdb.query). Attach Scheduled SQL Tasks (ScheduledSQLTask) to continuously transform live data streams into new dynamic tables at precise intervals.
Expose all internal Rill tables and real-time engine telemetry over native TCP/IP using the Quack Protocol. When enabled via engine.start(quack_port=9494, quack_token="secure_token"), Rill starts a background server thread that allows external tools, worker processes, and dashboards to securely query live PyArrow memory with zero-copy shared memory semantics.
Define Scheduled SQL Tasks (engine.add_dashboard_scheduled_query(...)) to dynamically extract and aggregate domain KPIs (e.g., maximum order tickets, cumulative revenue) in real-time. The dashboard instantly renders these live views as business metrics and evaluates threshold alerts against them.
Rill includes a full-stack alerts and observability pipeline that captures engine exceptions, user-pushed alerts, and threshold-based rule triggers into a persistent rill_alerts DuckDB table β visible in real-time on the dashboard.
- Structured Logging: Every module uses
logging.getLogger(__name__)with proper Python logging. No silentexcept: passblocks β all errors are logged and recorded. - Server-Side Alert Recording: All internal engine exceptions (connector failures, SQL task errors, memory budget warnings, business metric evaluation failures) are automatically recorded as structured alerts with severity (
error,warning,info), source component, and traceback detail. - User-Facing Alert API: Push custom alerts directly from application code:
# Primary method with full control engine.push_alert("Revenue dropped below threshold", severity="warning", detail="Current: $847") engine.push_alert("Payment gateway timeout", severity="error", detail=traceback.format_exc()) # Convenience methods engine.alert("Batch processed successfully") # severity="info" engine.warn("High latency detected on orders") # severity="warning" engine.error("Database connection failed") # severity="error"
- Dashboard Integration: All alerts (engine, user, and rule-based) appear in the Alert Logs view with severity badges (π΄ Error, π‘ Warning, π΅ Info), source badges (π€ User, βοΈ Engine, β‘ Rule), filter toggles, and toast notifications.
- Automatic Retention: Alerts older than 1 hour are automatically pruned during each metrics flush cycle.
Rill includes a state-of-the-art, dark-themed Live Business Intelligence & System Monitoring Dashboard built with Node.js, WebSocket Quack Bridge (quack_worker.py), and Chart.js.
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β NODE.JS / BROWSER DASHBOARD β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β π Key Business Intelligence & Scheduled Queries (Live Streaming) β β
β β [ max_order_amount ($): $497.34 ] [ total_revenue ($): $13,025.21 ] β β
β β [ cancelled_ratio: 26.5% ] β β
β β π Scheduled Line & Bar Charts evaluating Live SQL Data β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β βοΈ Engine Infrastructure & Resource Utilization β β
β β π» CPU: 37.6% | πΎ RAM: 3.6 GB | πΉ PyArrow: 12.47 MB | β‘ Latency: 17msβ β
β β π System Resource History (CPU / Memory Trend Graph) β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
ββββββββββββββββββββββββββββββββββββββββββ²βββββββββββββββββββββββββββββββββββββββββ
β WebSocket JSON Stream
ββββββββββββββββββββββββββββββββββββββββββΌβββββββββββββββββββββββββββββββββββββββββ
β NODE.JS QUACK BRIDGE SERVER β
β (dashboard/server.js + quack_worker.py) β
ββββββββββββββββββββββββββββββββββββββββββ²βββββββββββββββββββββββββββββββββββββββββ
β Native Quack Protocol (TCP Socket)
ββββββββββββββββββββββββββββββββββββββββββΌβββββββββββββββββββββββββββββββββββββββββ
β RILL STREAMING ENGINE (`quack:9494`) β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
- π Multi-View Glassmorphic Navigation: Seamlessly switch between the Metrics Console, Custom Queries, Alert Rules & Settings, and Alert Logs right from the top bar.
- π Business Intelligence Centerpiece: Highlights Custom Scheduled Queries mapped to Live Charts and extracts numeric results into business KPI metrics that trigger alerts instantaneously.
- π Custom SQL Queries & Chart Mapping: Write, execute, and schedule ad-hoc SQL queries against the on-disk
rill_metricsdatabase. Map query results on the fly to Interactive Line, Bar, and Scatter Charts with zoom/pan capabilities powered bychartjs-plugin-zoom. - βοΈ Separated System Infrastructure: Cleanly separates host CPU, System Memory, PyArrow C++ pool allocations, DuckDB storage size (
LivevsMetricsDB), throughput (RPS), and step latency into a dedicated secondary monitoring section. - π Unified Alert Logs: Displays engine-side exceptions, user-pushed alerts, and threshold rule triggers in a single unified view with severity badges (π΄π‘π΅), source badges (π€ User / βοΈ Engine / β‘ Rule), filter toggles (All / Errors / Warnings / User / Engine / Rules), and severity-colored toast notifications for real-time visibility.
- π Browser-Native Sound Alerts: Real-time Web Audio API acoustic engine with selectable tones (
Classic Beep,Warning Siren,Gentle Chime,Urgent Buzzer), customizable duration sliders (1sβ30s), and instant sound preview. - βͺ Offline / Disconnected Grey Tabs: Multi-tab connection bar with automatic health tracking. If a remote Quack server drops connection or exits, its tab instantly turns grey with a grey status dot and hover failure details.
- π Server-Side Webhook Relay (
POST /api/webhook/send): Bypasses browser CORS restrictions by routing real-time alert notifications directly to Slack Incoming Webhooks, Google Chat / Google Meet Cards, or Custom JSON endpoints. - β‘ Real-Time Threshold Rule Engine: Evaluates custom trigger rules (
>,>=,<,<=,==) on system and business metrics with cooldown protection. Rules and settings persist automatically inlocalStorage.
Install Rill locally or in editable mode with development & connector dependencies:
git clone https://github.com/lijose/rill.git
cd rill
python3 -m venv .env
source .env/bin/activate
pip install -e .Step 1: Launch the Rill engine with live simulated orders and custom business metrics:
python3 examples/quack_dashboard_demo.py(Note your quack:127.0.0.1:9494 address and auth token printed in the terminal).
Step 2: In a separate terminal, start the Node.js Dashboard Bridge:
cd dashboard
npm install
npm startStep 3: Open your browser to http://localhost:3000, enter quack:127.0.0.1:9494 and your token, and click Connect Console!
import time
import pyarrow as pa
import pyarrow.compute as pc
from rills import RillEngine, MemoryConnector, ScheduledSQLTask, RetentionPolicy, schema
# 1. Initialize Rill Engine with a 200ms micro-batch interval and 500 MB memory budget
engine = RillEngine(
trigger_interval_ms=200,
memory_budget_mb=500.0,
quack_address="quack:0.0.0.0:9494",
quack_token="secret_token"
)
# 2. Define schema with primary key and register table
orders_schema = schema([
("order_id", pa.int64()),
("user_tier", pa.string()),
("amount", pa.float64())
], primary_key="order_id", mode="snapshot")
engine.register_table("orders", schema=orders_schema)
# 3. Schedule a live dashboard query to compute business KPIs
engine.add_dashboard_scheduled_query(
name="Max Order Amount",
query="SELECT MAX(amount) as max_amount FROM orders",
interval_seconds=1.0,
chart_type="bar",
x_col="ts",
y_col="max_amount"
)
# 4. Attach memory connector to 'orders' table
connector = MemoryConnector(target_table="orders")
engine.add_connector(connector)
# 5. Add a Scheduled DuckDB SQL Query running every 1 second
sql_task = ScheduledSQLTask(
name="tier_revenue",
query="SELECT user_tier, SUM(amount) as total_revenue, COUNT(*) as order_count FROM orders GROUP BY user_tier",
output_table="revenue_by_tier",
interval_seconds=1.0
)
engine.add_sql_task(sql_task)
# 6. Start engine and Quack server (`quack:0.0.0.0:9494`)
engine.start()
# Push incoming events directly to connector
batch = pa.RecordBatch.from_pydict({
"order_id": [1, 2, 3],
"user_tier": ["Gold", "Silver", "Gold"],
"amount": [100.50, 45.00, 250.00]
}, schema=orders_schema)
connector.push(batch)
time.sleep(1.2)
# The live data is fully isolated to prevent locking issues.
# Telemetry, Scheduled Queries, and Alerts can be read safely via metrics_con:
alerts = engine.metrics_con.execute("SELECT * FROM rill_alerts LIMIT 5").df()
print("Recent System Alerts:\n", alerts)
engine.stop()from rills import RillEngine, MemoryConnector
import traceback
engine = RillEngine(
trigger_interval_ms=200,
quack_address="quack:0.0.0.0:9494"
)
engine.register_table("orders", primary_key="order_id")
connector = MemoryConnector(target_table="orders")
engine.add_connector(connector)
# Push custom alerts from your application code
engine.alert("Engine started successfully") # info severity
engine.warn("Order queue backlog exceeds threshold") # warning severity
try:
# Your business logic here
result = process_payment(order)
except Exception:
engine.error("Payment processing failed", detail=traceback.format_exc())
# Alerts with full control
engine.push_alert(
"Revenue dropped below $1,000",
severity="warning",
detail="Current daily revenue: $847.23. Threshold: $1,000."
)
engine.start()
# Engine exceptions (connector failures, SQL errors, memory warnings) are
# automatically recorded as alerts β no extra code needed.
# All alerts appear in the dashboard Alert Logs view.Alerts are stored in the rill_alerts DuckDB table and can also be queried directly:
results = engine.metrics_con.execute("""
SELECT ts, severity, source, message
FROM rill_alerts
WHERE severity = 'error'
ORDER BY ts DESC LIMIT 10
""").fetchall()See our complete end-to-end multi-stream demo inside examples/:
python3 examples/multi_stream_join_demo.pyThis demo illustrates:
- Joining a live
user_profilestable with a continuousordersstream intoorders_enriched. - Calculating regional revenue aggregations via DuckDB SQL every second.
Rill is verified by a comprehensive test suite (pytest) covering schema enrichment, primary key extraction, table processing modes, mandatory TTL validation, memory governance warnings, backpressure slicing, zero-copy joins, alerts pipeline, bug regression tests, and connector edge cases:
pytest tests/ -vTest coverage includes:
test_alerts_pipeline.pyβ Alert recording, DuckDB persistence, user API (push_alert,alert,warn,error), connector error alert generation, alert retentiontest_bug_fixes.pyβ Import sanity, connector edge cases (inconsistent rows, empty pushes, deque buffers), O(NΒ²) buffer fix, streaming JSON export, resource cleanuptest_engine.pyβ Step execution, callbacks, start/stop lifecycletest_connectors.pyβ Memory, JSON stream, and file connectorstest_backpressure.pyβ Bounded buffer overflow strategiestest_duckdb_sql.pyβ Zero-copy queries, scheduled SQL taskstest_stream_joins.pyβ Multi-stream join tasks with and without aggregationtest_schema.pyβ Schema enrichment, primary key extraction, row size estimationtest_retention.pyβ Max rows and max age TTL pruningtest_table_modes.pyβ Snapshot vs append mode behaviortest_upsert.pyβ Single/composite primary key upserts, append modetest_metrics.pyβ System and business metrics trackingtest_memory_budget.pyβ Memory budget warnings and callbacks
We welcome community contributions! Please review our CONTRIBUTING.md guide and CODE_OF_CONDUCT.md before opening pull requests or reporting issues.
Rill is open-sourced under the MIT License.