cosid-sharding · git:20260817.88affa1 · 2026-08-17 · sha256 b3e55850adda7bee

cosid-sharding git:20260817.88affa1A

Immutable. This exact content is served forever at /api/v1/blob/b3e55850adda7bee.

---
name: cosid-sharding
description: Design and configure CosId sharding algorithms for database sharding and ShardingSphere. Use when the user mentions table or database sharding, ShardingSphere COSID_MOD or COSID_INTERVAL rules, modulo sharding, date/time interval sharding, range routing, SnowflakeId timestamp extraction, ModCycle, IntervalTimeline, CachedSharding, PreciseSharding, RangeSharding, or SnowflakeLocalDateTimeConvertor.
---

# CosId Sharding Algorithms

Use this skill to choose, configure, and validate CosId sharding behavior.

## Workflow

1. Identify the sharding key type: numeric ID, SnowflakeId, `LocalDateTime`, or an existing timestamp column.
2. Choose the algorithm: `ModCycle` for uniform numeric distribution, `IntervalTimeline` for time ranges, or `CachedSharding` to cache repeated range routing.
3. Confirm both precise and range queries. ShardingSphere routes `=`, `IN`, and range predicates differently.
4. Define effective nodes and bounds explicitly. For interval sharding, include lower/upper datetime bounds and suffix format.
5. Provide a minimal Java or ShardingSphere YAML example and a routing test.

## Sharding Algorithm Types

| Algorithm | Class | Best For |
|---|---|---|
| **Modulo (ModCycle)** | `ModCycle<T>` | Uniform distribution, numeric IDs |
| **Interval Timeline** | `IntervalTimeline` | Date-based partitioning, time-series data |
| **Cached Sharding** | `CachedSharding<T>` | Wraps any algorithm to cache range lookups |

## Architecture

The sharding hierarchy:

```
Sharding<T>              (combines precise + range)
├── PreciseSharding<T>   (single value → node)
└── RangeSharding<T>     (value range → collection of nodes)

Implementations:
├── ModCycle<T>          (modulo-based, numeric IDs)
├── IntervalTimeline     (time-based intervals)
└── CachedSharding<T>    (caching decorator)
```

## ModCycle - Modulo Sharding

Distributes numeric IDs across nodes using `value % divisor`. Best for uniform distribution when using SnowflakeId or SegmentId.

Use `ModCycle` when the sharding key is already numeric and the desired distribution is even across a fixed number of tables or databases.

**Constraints:**
- Sharding values must be non-negative (SnowflakeId and SegmentId IDs are non-negative); a negative value yields a negative modulo remainder and throws `ArrayIndexOutOfBoundsException` today.
- Range queries resolve by span: any span covering at least `divisor` values returns all nodes. "Unbounded" ranges such as `Range.closed(0L, Long.MAX_VALUE)` route to all nodes (overflow-safe since 3.2.1).

### Usage

```java
import me.ahoo.cosid.sharding.ModCycle;

// Shard across 4 nodes: table_0, table_1, table_2, table_3
ModCycle<Long> sharding = new ModCycle<>(4, "table_");

// Precise sharding
String node = sharding.sharding(42L);  // → "table_2"

// Range sharding
Range<Long> range = Range.closed(1L, 10L);
Collection<String> nodes = sharding.sharding(range);  // all 4 nodes
```

### ShardingSphere Integration

```yaml
# ShardingSphere YAML configuration
rules:
  - !SHARDING
    tables:
      t_order:
        actualDataNodes: ds_${0..1}.t_order_${0..3}
        tableStrategy:
          standard:
            shardingColumn: order_id
            shardingAlgorithmName: t_order_mod
    shardingAlgorithms:
      t_order_mod:
        type: COSID_MOD
        props:
          divisor: 4
          logic-name-prefix: t_order_
```

## IntervalTimeline - Time-Based Sharding

Distributes data across time-based intervals. Each interval maps to a specific node named with a formatted date suffix.

Use `IntervalTimeline` when table names encode time periods such as day, month, or hour. It is also appropriate when a SnowflakeId can be converted back into event time.

### Usage

```java
import me.ahoo.cosid.sharding.IntervalTimeline;
import me.ahoo.cosid.sharding.IntervalStep;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import com.google.common.collect.Range;

// Daily sharding for 2024
IntervalTimeline<LocalDateTime> timeline = new IntervalTimeline<>(
    "t_order_",                                                    // logic name prefix
    Range.closed(
        LocalDateTime.of(2024, 1, 1, 0, 0),
        LocalDateTime.of(2024, 12, 31, 23, 59, 59)
    ),
    IntervalStep.of(ChronoUnit.DAYS),                              // daily intervals
    DateTimeFormatter.ofPattern("yyyyMMdd")                         // suffix format
);

// Precise: which table holds data for 2024-03-15?
String node = timeline.sharding(LocalDateTime.of(2024, 3, 15, 10, 30));
// → "t_order_20240315"

// Range: which tables cover March 2024?
Range<LocalDateTime> marchRange = Range.closed(
    LocalDateTime.of(2024, 3, 1, 0, 0),
    LocalDateTime.of(2024, 3, 31, 23, 59, 59)
);
Collection<String> nodes = timeline.sharding(marchRange);
// → ["t_order_20240301", "t_order_20240302", ..., "t_order_20240331"]
```

### Interval Step Options

```java
// Yearly intervals
IntervalStep.of(ChronoUnit.YEARS)

// Monthly intervals
IntervalStep.of(ChronoUnit.MONTHS)

// Daily intervals
IntervalStep.of(ChronoUnit.DAYS)

// Hourly intervals
IntervalStep.of(ChronoUnit.HOURS)

// Custom: every 3 months
IntervalStep.of(ChronoUnit.MONTHS, 3)
```

### Common Suffix Formatters

```java
// Yearly: t_order_2024, t_order_2025
DateTimeFormatter.ofPattern("yyyy")

// Monthly: t_order_202401, t_order_202402
DateTimeFormatter.ofPattern("yyyyMM")

// Daily: t_order_20240315
DateTimeFormatter.ofPattern("yyyyMMdd")

// Hourly: t_order_2024031514
DateTimeFormatter.ofPattern("yyyyMMddHH")
```

### ShardingSphere Integration for Interval Sharding

```yaml
rules:
  - !SHARDING
    tables:
      t_order:
        actualDataNodes: ds_0.t_order_${20240101..20241231}
        tableStrategy:
          standard:
            shardingColumn: create_time
            shardingAlgorithmName: t_order_interval
    shardingAlgorithms:
      t_order_interval:
        type: COSID_INTERVAL
        props:
          logic-name-prefix: t_order_
          datetime-lower: "2024-01-01 00:00:00"
          datetime-upper: "2024-12-31 23:59:59"
          sharding-suffix-pattern: yyyyMMdd
          datetime-interval-unit: DAYS
          datetime-interval-amount: 1
```

## SnowflakeLocalDateTimeConvertor

Converts SnowflakeId values to LocalDateTime for time-based sharding using SnowflakeId as the sharding key. It wraps a `SnowflakeIdStateParser` (built from the generator, so epoch and bit layout stay aligned) and accepts either a numeric ID or a Radix62 string:

```java
import me.ahoo.cosid.sharding.SnowflakeLocalDateTimeConvertor;
import me.ahoo.cosid.snowflake.SnowflakeIdStateParser;

SnowflakeLocalDateTimeConvertor convertor = new SnowflakeLocalDateTimeConvertor(
    SnowflakeIdStateParser.of(snowflakeId)  // your configured SnowflakeId
);

// Convert a SnowflakeId (Long or Radix62 String) to LocalDateTime
LocalDateTime time = convertor.toLocalDateTime(snowflakeId.generate());
```

This enables using SnowflakeId-based IDs with IntervalTimeline sharding without a separate timestamp column.

## CachedSharding

Wraps any sharding algorithm to cache range sharding results:

```java
import me.ahoo.cosid.sharding.CachedSharding;

ModCycle<Long> modSharding = new ModCycle<>(32, "table_");
CachedSharding<Long> cachedSharding = new CachedSharding<>(modSharding);

// First range query computes and caches
Collection<String> nodes1 = cachedSharding.sharding(Range.closed(1L, 100L));

// Subsequent identical range queries use cache
Collection<String> nodes2 = cachedSharding.sharding(Range.closed(1L, 100L));
```

Range queries are often repeated (e.g., querying "last 7 days" across many requests), so caching avoids redundant computation.

## Choosing a Sharding Strategy

| Scenario | Algorithm | Why |
|---|---|---|
| Uniform numeric ID distribution | ModCycle | Simple, even distribution |
| Date-based table partitioning | IntervalTimeline | Maps time ranges to tables |
| SnowflakeId as sharding key | IntervalTimeline + SnowflakeLocalDateTimeConvertor | Extract timestamp from ID |
| High QPS range queries | CachedSharding + any | Cache avoids recomputation |
| Auto-increment / SegmentId as key | ModCycle | Even distribution of monotonic IDs |

## Validation Checklist

Use a small routing matrix before finalizing a rule:

- One exact key routes to exactly one expected node.
- Sharding keys are non-negative for `ModCycle`; test with real generated IDs, not hand-picked negative values.
- An `IN` query routes to the union of expected nodes.
- A range query covers all boundary nodes and no unrelated nodes when possible.
- Values outside an `IntervalTimeline` effective range fail intentionally.
- Snowflake timestamp extraction uses the same epoch and timestamp bits as the generator.
- The ShardingSphere `actualDataNodes` expression matches every possible CosId effective node.

## Key Design Principles

1. **Precise + Range**: Every algorithm supports both single-value and range sharding. ShardingSphere uses precise for `=` and `IN`, and range for `BETWEEN`, `>`, `<`.
2. **Effective nodes**: `getEffectiveNodes()` returns all possible target nodes. This is used by ShardingSphere for routing optimization.
3. **Thread safety**: The `Sharding` interface is annotated `@ThreadSafe`; implementations follow that contract, but not every concrete class repeats the annotation.
4. **Interval bounds**: `IntervalTimeline` requires an explicit effective time range. Values outside this range throw `IllegalArgumentException`.
5. **Generator alignment**: When the sharding key is a CosId-generated ID, keep the generator epoch, timestamp unit, and converter settings aligned with the sharding rule.

## Response Template

When answering a sharding request, include:

1. The selected algorithm and why it fits the sharding key.
2. The expected table/database naming pattern.
3. A concise Java or ShardingSphere YAML example.
4. A routing test matrix for exact, `IN`, and range queries.