cosid-sharding · diff

git:20260817.88affa1 to git:20260818.2ff7b22

62 added, 204 removed. Audit A to A.

---
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.
+ description: Design, implement, and validate CosId database sharding with core ModCycle, IntervalTimeline, CachedSharding, and SnowflakeLocalDateTimeConvertor APIs or ShardingSphere COSID_MOD, COSID_INTERVAL, and COSID_INTERVAL_SNOWFLAKE algorithms. Use for modulo routing, date/time partitions, Snowflake timestamp routing, exact/IN/range behavior, effective nodes, suffix naming, or routing tests. Do not use for choosing an ID generator unless sharding is the primary concern.
---
- # CosId Sharding Algorithms
+ # Design CosId Sharding
- Use this skill to choose, configure, and validate CosId sharding behavior.
+ Treat core Java sharding APIs and ShardingSphere configuration as separate integration surfaces. Verify the target CosId and ShardingSphere versions before copying property names.
## 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.
+ 1. Identify the sharding key type and every query operator that must route: exact, `IN`, and range.
+ 2. Define physical nodes, naming, bounds, and future partition provisioning before selecting an algorithm.
+ 3. Choose `ModCycle` for fixed-count numeric distribution or `IntervalTimeline` for time partitions.
+ 4. Add Snowflake conversion only when the ID layout is known and aligned with the parser.
+ 5. Build a routing matrix that covers boundaries, out-of-range values, and full-range queries.
- ## Sharding Algorithm Types
+ ## Core Algorithms
- | Algorithm | Class | Best For |
+ | API | Use | Important behavior |
|---|---|---|
- | **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).
+ | `ModCycle<T>` | Fixed number of numeric nodes | Exact route is `value % divisor`; current implementation requires non-negative values |
+ | `IntervalTimeline` | Bounded `LocalDateTime` partitions | Exact values outside the effective interval throw `IllegalArgumentException` |
+ | `SnowflakeLocalDateTimeConvertor` | Convert Snowflake `Long` or radix-62 `String` to time | Parser must match the generator epoch and bit layout |
+ | `CachedSharding<T>` | Reuse identical range results | `@Beta` and backed by an unbounded cache; use only for demonstrably low-cardinality repeated ranges |
- ### Usage
+ ### Modulo Example
```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
+ ModCycle<Long> sharding = new ModCycle<>(4, "t_order_");
- ```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_
+ String exact = sharding.sharding(42L); // t_order_2
+ Collection<String> range = sharding.sharding(Range.closed(1L, 10L));
```
- ## 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.
+ A range spanning at least `divisor` consecutive values routes to every node. Open and unbounded ranges must be included in tests. Do not pass negative values unless the implementation has been changed to use floor-mod semantics.
- ### Usage
+ ### Interval Example
```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
+ IntervalTimeline timeline = new IntervalTimeline(
+ "t_order_",
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
+ IntervalStep.of(ChronoUnit.MONTHS),
+ DateTimeFormatter.ofPattern("yyyyMM")
);
- // 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(
+ String exact = timeline.sharding(LocalDateTime.of(2024, 3, 15, 10, 30));
+ Collection<String> range = timeline.sharding(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"]
+ LocalDateTime.of(2024, 4, 30, 23, 59, 59)
+ ));
```
- ### 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)
- ```
+ Supported `IntervalStep` units are years, months, days, hours, minutes, and seconds. Ensure the suffix format is unique for the selected step.
- ### Common Suffix Formatters
+ ### Snowflake Conversion
```java
- // Yearly: t_order_2024, t_order_2025
- DateTimeFormatter.ofPattern("yyyy")
+ SnowflakeIdStateParser parser = SnowflakeIdStateParser.of(snowflakeId);
+ SnowflakeLocalDateTimeConvertor convertor =
+ new SnowflakeLocalDateTimeConvertor(parser);
- // Monthly: t_order_202401, t_order_202402
- DateTimeFormatter.ofPattern("yyyyMM")
+ LocalDateTime time = convertor.toLocalDateTime(snowflakeId.generate());
+ ```
- // Daily: t_order_20240315
- DateTimeFormatter.ofPattern("yyyyMMdd")
+ The converter accepts only `Long` and radix-62 `String` values. It is not a general converter for arbitrary numeric types or custom string encodings.
- // Hourly: t_order_2024031514
- DateTimeFormatter.ofPattern("yyyyMMddHH")
- ```
+ ## ShardingSphere
- ### ShardingSphere Integration for Interval Sharding
+ CosId algorithm availability and properties vary by ShardingSphere release. Inspect the target artifact or official source before emitting a type. When `COSID_MOD` is available, its divisor property is named `mod`, not `divisor`:
```yaml
rules:
- !SHARDING
tables:
t_order:
- actualDataNodes: ds_0.t_order_${20240101..20241231}
+ actualDataNodes: ds_0.t_order_$->{0..3}
tableStrategy:
standard:
- shardingColumn: create_time
- shardingAlgorithmName: t_order_interval
+ shardingColumn: order_id
+ shardingAlgorithmName: t_order_mod
shardingAlgorithms:
- t_order_interval:
- type: COSID_INTERVAL
+ t_order_mod:
+ type: COSID_MOD
props:
+ mod: 4
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 |
+ For ordinary time keys, configure an available `COSID_INTERVAL` implementation with `datetime-lower`, `datetime-upper`, `sharding-suffix-pattern`, `datetime-interval-unit`, and `datetime-interval-amount`.
- ## Validation Checklist
+ Handle Snowflake interval routing by version:
- Use a small routing matrix before finalizing a rule:
+ - Prefer the core API shown above when application code owns routing; `SnowflakeIdStateParser.of(snowflakeId)` follows the actual generator's epoch and bit layout.
+ - In Apache ShardingSphere 5.4.0, `COSID_INTERVAL_SNOWFLAKE` reads only `epoch` and `zone-id` and hardcodes the default millisecond Snowflake layout. Use it only for that layout; `id-name` does not align a custom generator.
+ - For every other ShardingSphere release, verify that the CosId plugin and requested properties exist. Do not emit `COSID_INTERVAL_SNOWFLAKE` merely because an older document lists it.
+ - For a custom bit layout, use a version-verified integration that resolves the actual generator or a minimal `StandardShardingAlgorithm` that delegates to `SnowflakeIdStateParser` and `IntervalTimeline`.
- - 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.
+ Ensure `actualDataNodes` enumerates only valid physical suffixes; a daily numeric range such as `${20240101..20241231}` also generates impossible dates and must not be used.
- ## Key Design Principles
+ ## Validation Matrix
- 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.
+ Test at least:
- ## Response Template
+ - first and last exact key for every boundary node;
+ - one `IN` set spanning multiple nodes;
+ - closed, open, unbounded, empty, and out-of-domain ranges;
+ - negative input rejection for `ModCycle`;
+ - an interval transition such as month-end or year-end;
+ - Snowflake values generated with the actual epoch, unit, and bit layout;
+ - equality between algorithm effective nodes and provisioned physical nodes.
- When answering a sharding request, include:
+ ## Response Contract
- 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.
+ Return the chosen algorithm, physical naming model, minimal Java or version-matched ShardingSphere configuration, and the routing matrix. Call out any partition provisioning or cache-growth risk explicitly.