--- name: rill-development description: Overview of how to develop a Rill project --- # Instructions for developing a Rill project This document is intended for data engineering agents specialized in developing projects in the Rill business intelligence platform. ## Introduction to Rill Rill is a business intelligence platform built around the following principles: - Code-first: configure projects using versioned and reproducible source code in the form of YAML and SQL files. - Full stack: go from raw data sources to user-friendly dashboards powered by clean data with a single tool. - Declarative: describe your business logic and Rill automatically runs the infrastructure, migrations and services necessary to make it real. - OLAP databases: you can easily provision a fast analytical database and load data into it to build dashboards that stay interactive at scale. ## Project structure A Rill project consists of resources that are defined using YAML and SQL files in the project's file directory. Rill supports different resource types, such as connectors, models, metrics views, explore dashboards, and more. Here is an example listing of files for a small Rill project: ``` .env connectors/duckdb.yaml connectors/s3.yaml models/events_raw.yaml models/events.sql metrics/events.yaml rill.yaml ``` Note that there is no separate dashboard file: the explore dashboard for `metrics/events.yaml` is defined inline in the metrics view file (see the "Explores" section below). Let's start with the project-wide files at the root of the directory: - `rill.yaml` is a required file that contains project-wide configuration. It can be compared to `package.json` in Node.js or `dbt_project.yml` in dbt. - `.env` is an optional file containing environment variables, usually secrets such as database credentials. The other YAML and SQL files define individual resources in the project. They follow a few rules: - The YAML files must contain a `type:` property that identifies the resource type. The other properties in the file are specific to the selected resource type. - SQL files are a convenient way of creating model resources. They are equivalent to a YAML file with `type: model` and a `sql:` property. - Each file declares one main resource, but may in some cases also emit some dependent resources with internally generated names. - The main resource declared by a file gets a unique name derived from the filename by removing the directory name and extension. For example, `connectors/duckdb.yaml` defines a connector called `duckdb`. - Directories are ignored by the parser and can be used to organize the project as you see fit. Small projects often have one directory per resource type. - Resources can reference other resources, which forms a dependency graph (DAG) that informs the sequence they are executed. - Resource names are unique within a resource type. For example, only one model can be named `events` (regardless of directory), but it is possible for both a model and a metrics view to be called `events`. - Clear resource names are important as they are widely used as unique identifiers throughout the platform (e.g. in CLI commands, URL slugs, API calls). They are usually lowercase and snake case, but that is not enforced. - Any resource can carry a top-level `tags:` list (free-form labels) for organizing and filtering resources across a project. Distinct from dimension/measure tags inside a metrics view. ## Project execution Rill automatically watches project files and processes changes. There are two key phases: - **Parsing**: Files are converted into resources and organized into a DAG. Malformed files produce *parse errors*. - **Reconciliation**: Resources are executed to achieve their desired state. Failures produce *reconcile errors*. Some resources are cheap to reconcile (validation, non-materialized models), others are expensive (materialized models, managed connectors). Be cautious with expensive operations; see resource-specific instructions for details. Resources can also have scheduled reconciliation via cron expressions (e.g. daily model refresh). ## Rill's environments Rill has a local CLI (`rill`) for development and a cloud service for production. After developing or changing a project locally, developers deploy to Rill Cloud either by pushing to GitHub (continuous deploys) or manually deploying with the CLI. ## OLAP databases Rill places high emphasis on "operational intelligence", meaning low-latency, high-performance, drill-down dashboards with support for alerts and scheduled reports. Rill supports these features using OLAP databases and has drivers that are heavily optimized to leverage database-specific features to get high performance. OLAP databases are configured as any other connector in Rill. People can either connect an external OLAP database with existing tables, or can ask Rill to provision an empty OLAP database for them, which they can load data into using Rill's `model` resource type. OLAP connectors are currently the only connectors that can directly power the metrics views resources that in turn power dashboards. So data must be in an OLAP database to power a dashboard. Since OLAP databases have a special role in Rill, every project must have a _default_ OLAP connector that you configure using the `olap_connector:` property in `rill.yaml`. This default OLAP connector is automatically used for a variety of things in Rill unless explicitly overridden (see details under the resource type descriptions). If no OLAP connector is configured, Rill by default initializes a managed `duckdb` OLAP database and uses it as the default OLAP connector. ## Resource types The sections below contain descriptions of the different resource types that Rill supports and when to use them. The descriptions are high-level; you can find detailed descriptions and examples in the separate resource-specific instruction files. ### Connectors Connectors are resources containing credentials and settings for connecting to an external system. They are usually lightweight as their reconcile logic usually only validates the connection. They are normally found at the root of the DAG, powering other downstream resource types. There are a variety of built-in connector _drivers_, which each implements one or more capabilities: - **OLAP database:** can power dashboards (e.g. `duckdb`, `clickhouse`, `druid`, `pinot`, `starrocks`, and `snowflake`, `bigquery`, `databricks` as live connectors) - **SQL database:** can run SQL queries and models (e.g. `postgres`, `bigquery`, `snowflake`) - **Information schema:** can list tables and their schemas (e.g. `duckdb`, `bigquery`, `postgres`) - **Object store:** can list, read and write flat files (e.g. `s3`) - **Notifier:** can send notifications (e.g. `slack`) - **AI:** can generate embeddings or responses (e.g. `openai`, `claude`, `gemini`) Here are some useful things to know when developing connectors: - Actual secrets like database passwords should go in `.env` and be referenced from the connector's YAML file - Connectors are usually called the same as their driver, unless there are multiple connectors that use the same driver. - OLAP connectors with the property `managed: true` will automatically be provisioned by Rill, so you don't need to handle the infrastructure or credentials directly. This is only supported for the `duckdb` and `clickhouse` drivers. The user will be subject to usage-based billing for the CPU, memory and disk usage of the provisioned database. - User-configured OLAP connectors with externally managed tables should have `mode: read` to protect from unintended writes from Rill models. - The primary OLAP connector used in a project should be configured in `rill.yaml` using the `olap_connector:` property. ### Models Models are resources that specify ETL or transformation logic that outputs a tabular dataset in one of the project's connectors. They are usually expensive resources that are found near the root of the DAG, referencing only connectors and other models. Models usually (and by default) output data as a table with the same name as the model in the project's default OLAP connector. They usually center around a `SELECT` SQL statement that Rill will run as a `CREATE TABLE AS