# How Region Partitioning Works in GreptimeDB for Horizontal Scaling

> Discover how GreptimeDB achieves horizontal scaling with region partitioning and pluggable rules to distribute data efficiently across datanodes for better performance.

- Repository: [Greptime/greptimedb](https://github.com/greptimeteam/greptimedb)
- Tags: internals
- Published: 2026-03-02

---

**GreptimeDB achieves horizontal scaling by dividing logical tables into independent regions distributed across datanodes, using pluggable partition rules that deterministically map rows to region numbers based on multi-dimensional expressions.**

GreptimeDB stores each logical table as a collection of regions—autonomous storage units that can reside on different datanodes. The mapping from row data to specific regions is governed by a **partition rule** defined at table creation and stored in the table's metadata. This architecture allows the database to distribute write and read workloads across a cluster while supporting online rebalancing.

## Core Architecture: Regions and Partition Rules

### The Region Abstraction

In `greptimeteam/greptimedb`, a **region** represents an independent storage unit containing a subset of a table's data. Regions can be placed on different datanodes, enabling data to spread across the cluster. Each region is identified by a `RegionNumber`, and the collection of regions forms the complete table.

### The PartitionRule Trait

The core abstraction for mapping data to regions is the `PartitionRule` trait defined in [`src/partition/src/partition.rs`](https://github.com/greptimeteam/greptimedb/blob/main/src/partition/src/partition.rs). This trait abstracts the conversion from a row's partition-key values to its target `RegionNumber`. The default implementation follows the multi-dimensional partition rule design described in the RFC *"multi-dimension partition rule"*:

```rust
// src/partition/src/partition.rs#L29-L40
pub trait PartitionRule: Send + Sync {
    fn partition(
        &self,
        values: &[Value],
    ) -> Result<PartitionResult, PartitionError>;
    
    fn find_regions(
        &self,
        exprs: &[Expr],
    ) -> Result<Vec<RegionNumber>, PartitionError>;
}

```

## Multi-Dimensional Partition Rules

The concrete implementation `MultiDimPartitionRule` in [`src/partition/src/multi_dim.rs`](https://github.com/greptimeteam/greptimedb/blob/main/src/partition/src/multi_dim.rs) evaluates user-defined expressions to determine region placement.

### Rule Evaluation Logic

`MultiDimPartitionRule` holds the allowed partition columns, a list of expressions (`exprs`), and the target region numbers (`regions`). For each incoming row, the rule evaluates expressions in order; the first matching expression yields its assigned region. If no expressions match, the row routes to the special **default region** (`0`):

```rust
// src/partition/src/multi_dim.rs#L102-L119
fn partition(&self, values: &[Value]) -> Result<PartitionRuleResult> {
    for (idx, expr) in self.exprs.iter().enumerate() {
        if expr.eval(values)? {
            return Ok(PartitionRuleResult {
                region: self.regions[idx],
            });
        }
    }
    // Default region when no expressions match
    Ok(PartitionRuleResult { region: 0 })
}

```

### Expression Matching

Partition expressions support multi-dimensional predicates combining multiple columns (e.g., `host = 'us-west' AND time < '2025-01-01'`). The expressions are evaluated sequentially, making the order of definition significant for routing logic.

## Region Routing and Metadata Management

The `PartitionRuleManager` in [`src/partition/src/manager.rs`](https://github.com/greptimeteam/greptimedb/blob/main/src/partition/src/manager.rs) serves as the central coordinator for retrieving and caching partition metadata from the meta-server.

### Key APIs

The manager exposes two critical methods for cluster operations:

- **`find_table_partition_rule`** – Returns the partition rule and a map of region-to-rule-version. The frontend uses this to attach version stamps to write requests, ensuring consistency during reconfiguration (lines 78-99).

- **`batch_find_region_routes`** – Obtains `RegionRoute` structures for query planning, allowing the distributed planner to target only relevant regions (lines 108-126).

## Write Path: Row Splitting and Distribution

### RowSplitter Implementation

When clients insert data, the `RowSplitter` in [`src/partition/src/splitter.rs`](https://github.com/greptimeteam/greptimedb/blob/main/src/partition/src/splitter.rs) groups rows by their target regions before network transmission. The splitter:

1. Extracts partition column values from each row
2. Consults the `PartitionRule` to determine the `RegionNumber`
3. Aggregates rows into a `HashMap<RegionNumber, Rows>`

```rust
// src/partition/src/splitter.rs#L34-L50
pub fn split(&self, rows: Rows) -> Result<HashMap<RegionNumber, Rows>> {
    let mut batches: HashMap<RegionNumber, Vec<Row>> = HashMap::new();
    
    for row in rows.rows {
        let values = self.extract_partition_values(&row)?;
        let region = self.rule.partition(&values)?;
        batches.entry(region).or_default().push(row);
    }
    // Convert to final format...
}

```

This batching minimizes cross-datanode traffic by grouping all rows for region 0 together, all rows for region 1 together, and so on.

## Horizontal Scaling Through Region Migration and Repartition

GreptimeDB supports two primary mechanisms for horizontal scaling without downtime: **region migration** and **repartition**.

### Region Migration

Region migration moves an existing region to a different datanode without changing its partition boundaries. The procedure is coordinated by `MetaSrv` and exercised in the integration test [`tests-integration/tests/region_migration.rs`](https://github.com/greptimeteam/greptimedb/blob/main/tests-integration/tests/region_migration.rs). During migration:

1. `MetaSrv` allocates the target datanode
2. The region data transfers to the new peer
3. The `RegionRoute` table updates atomically
4. Subsequent queries route to the new location

### Repartitioning

When data grows unevenly, administrators can **repartition** by splitting or merging regions. This workflow:

1. Creates new regions with updated boundaries
2. Rewrites table manifests with the new partition rule
3. Updates the meta-server's route table
4. Increments the partition-rule version

The detailed procedure is documented in [`docs/rfcs/2025-06-20-repartition.md`](https://github.com/greptimeteam/greptimedb/blob/main/docs/rfcs/2025-06-20-repartition.md) and validated in [`tests-integration/tests/repartition.rs`](https://github.com/greptimeteam/greptimedb/blob/main/tests-integration/tests/repartition.rs). Write requests carrying outdated version stamps are rejected, ensuring new data only flows to correctly placed regions.

```rust
// Conceptual repartition workflow
let sql = r#"
ALTER TABLE demo 
PARTITION BY (host, time) (
    host = 'us-west' AND time < '2025-06-01' => REGION 0,
    host = 'us-west' AND time >= '2025-06-01' => REGION 2,  -- new split
    host = 'us-east' => REGION 1
);
"#;

```

## Read Path Optimization

The query planner leverages `PartitionRuleManager` to build region-aware execution plans. When filter predicates match partition columns, the planner pushes down these filters to the partition rule, scanning only regions that could contain matching data. This optimization is detailed in the distributed planner RFC [`docs/rfcs/2023-05-09-distributed-planner.md`](https://github.com/greptimeteam/greptimedb/blob/main/docs/rfcs/2023-05-09-distributed-planner.md).

## Summary

- **Regions** are independent storage units distributed across datanodes, forming the horizontal scaling unit in GreptimeDB.
- **PartitionRule** trait abstracts the mapping logic, with `MultiDimPartitionRule` providing multi-dimensional expression evaluation.
- **RowSplitter** groups incoming writes by region before network transmission, optimizing the write path.
- **PartitionRuleManager** caches metadata and provides routing information for both write and read operations.
- **Region migration** moves regions between nodes; **repartition** splits or merges regions to rebalance data.
- Version stamps on partition rules ensure consistency during topology changes, rejecting stale writes.

## Frequently Asked Questions

### How does GreptimeDB decide which region stores a specific row?

GreptimeDB evaluates the table's **partition rule**—specifically the `MultiDimPartitionRule` implementation—which checks the row's partition column values against a list of expressions in [`src/partition/src/multi_dim.rs`](https://github.com/greptimeteam/greptimedb/blob/main/src/partition/src/multi_dim.rs). The first matching expression determines the `RegionNumber`; if none match, the row routes to default region `0`.

### What happens when a region becomes too large or receives too much traffic?

Administrators can trigger **repartition** to split the region into smaller ranges or **region migration** to move it to a less loaded datanode. Both operations are coordinated by `MetaSrv`, update the route table atomically, and increment the partition-rule version to ensure write consistency.

### Can partition rules be modified after table creation?

Yes. GreptimeDB supports `ALTER TABLE` statements to modify partition expressions, implemented through the repartition procedure defined in [`docs/rfcs/2025-06-20-repartition.md`](https://github.com/greptimeteam/greptimedb/blob/main/docs/rfcs/2025-06-20-repartition.md). The system creates new regions, migrates data, and updates metadata without requiring table downtime.

### How does the query optimizer use region partitioning?

The distributed planner queries `PartitionRuleManager` to obtain `RegionRoute` information. When SQL filters align with partition columns, the planner pushes predicates down to the partition rule evaluation, scanning only regions that could contain matching rows rather than the full table.